Points to note in Spring transaction management

Most people must be confused when they first come into contact with database transactions, and their confused look reveals their thirst for knowledge. Here I will give you some popular science knowledge. :

  • transaction concept
  • transaction characteristics
  • Mysql database storage engine support
  • Modify Mysql database storage engine
  • spring configuration transaction
  • test code
  • Notice

transaction concept

Transaction is a unit of concurrency control and a user-defined sequence of operations. Either all of these operations are done, or none of them are done, and they are an integral unit of work. Transactions bind together a logically related set of operations so that the server maintains data integrity.
COMMIT: Indicates submission, that is, all operations of committing a transaction. Specifically, all updates to the database in the transaction are written back to the physical database on the disk, and the transaction ends normally.
ROLLBACK means rollback, that is, some kind of failure occurs while the transaction is running and the transaction cannot continue. The system undoes all completed operations on the database in the transaction and rolls back to the state where the transaction started.

ex: A uses online banking to transfer money online to B <This is a scenario>
First case: A user's amount decreases and B user's amount increases <normal case>
Second case: A user's amount decreases and an exception occurs on the way B user's amount No increase <abnormal situation>
The third situation: User A's amount does not decrease but user B's amount increases <abnormal situation>
. . .
Except for the first case, the rest are exceptions; in this case, transactions must be used. Either do it or don't do it , that is: if account A decreases, account B must increase, otherwise account A will not decrease.

transaction characteristics

The four characteristics of transactions (ACID for short): Atomicity, Consistency, Isolation, and Durability

Atomicity: All operations in a transaction are indivisible in the database, either all are completed or none are executed.

Consistency: The execution results of several transactions executed in parallel must be consistent with the results of serial execution in a certain order.

Isolation: The execution of a transaction is not interfered with by other transactions, and the intermediate results of transaction execution must be transparent to other transactions.

Durability: For any committed transaction, the system must ensure that the changes to the database made by the transaction are not lost, even if the database fails.

Mysql database storage engine support

There are four storage engines of Mysql: MyIsAm, InnoDB, MEMORY, and MERGE. Each of the four has its own benefits. Technology selection can choose different methods according to different needs. MyIsAm
MyISAM is the default storage engine for MySQL. MyISAM does not support transactions or foreign keys, but its access speed is fast and there is no requirement for transaction integrity.
MyISAM tables also support 3 different storage formats
static table
Static tables are the default storage format. The fields in static tables are non-variable-length fields. The
advantages are: storage is very fast, easy to cache, and easy to recover in case of failure. The
disadvantages are: it usually takes up more space than dynamic tables. (Note: During storage, when the column width is insufficient, spaces are used to make up for it. These spaces will not be obtained when accessing)
dynamic table
The fields of dynamic tables are variable-length. The advantage is that they take up relatively little space. However, frequent updates and deletions of records will cause fragmentation. Performance needs to be improved regularly,
and recovery in the event of a failure is relatively difficult.
Compressed table
Compressed tables occupy small disk space, and each record is compressed individually, so there is very little access overhead.
InnoDB
The InnoDB storage engine provides transaction safety with commit, rollback, and crash recovery capabilities. However, compared to the MyISAM storage engine,
InnoDB writes less efficiently and takes up more disk space to retain data and indexes.
Moreover, MySQL supports only InnoDB as a foreign key storage engine. When creating a foreign key, the
attached table must have a corresponding index, and the sub-table will automatically create a corresponding index when creating a foreign key. (The foreign key of the related table must be the primary key of the related table)
InnoDB is ideal for use in tables with high concurrency and many update operations. Tables that need to use transactions. Tables with requirements for automated disaster recovery.
MEMORY
The MEMORY storage engine uses content stored in memory to create tables.
Each MEMORY table actually corresponds to only one disk file. MEMORY type table access is very fast because its data is stored in memory and HASH indexes are used by default.
But once the service is shut down, the data in the table will be lost. Memory storage engine is used in situations where fast speed is required and temporary data is required.
MERGE
The merge storage engine is a combination of a group of MyISAM tables. The structures of these MyISAM tables must be exactly the same. There is no data in the MERGE table. The MERGE
type table can be queried, updated, and deleted. These operations are actually performed on the internal MyISAM. table to operate. For the insert operation into the MERGE table, the inserted table is defined according to the INSERT_METHOD clause. It can have three different values. The first and last values ​​make the insert operation act on the first or last table accordingly. Define this clause or set it to NO,
indicating that the MERGE table cannot be inserted. You can perform a drop operation on the MERGE table. This operation only deletes the definition of the MERGE table and has no impact on the internal tables. MERGE retains two files starting with the MERGE table name on the disk: the .frm file stores the definition of the table; the .MRG file contains information about the combined table, including which tables the MERGE table is composed of, and the basis for inserting data.
The MERGE table can be modified by modifying the .MRG file, but it must be refreshed by flushing the table after modification.

The mainstream engine methods of Mysql are these four, and you can choose the corresponding one according to the conditions.

Modify Mysql database storage engine

show engines; #显示数据库是否支持InnoDB
Change method 1: Modify the configuration file my.cnf
Open my.cnf, add default-storage-engine=InnoDB at the end of [mysqld], restart the database service, and change the default engine of the database to InnoDB.
Change method 2: Specify when creating the table
create table tableName( id int primary key, name varchar(50) )type=InnoDB;
Change method 3: Modify after creating the table
alter table tableName ENGINE=InnoDB; #mysql5.0以后用这种方式
alter table tableName type = InnoDB; #mysql5.0之前用这种方式

spring configuration transaction

Here are two methods, one is the AOP automatic injection method, and the other is the annotation-based method.

Automatic injection method
During AOP crosscutting, transactions are injected where needed according to the rules. The result of this is that unnecessary places may also be injected. Causes a waste of resources. The advantage is that you don't have to forget it, and you don't have to worry about whether there are places that are not managed by the transaction.
    <!-- 配置数据源 使用的是Druid数据源 -->
    <bean name="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
        init-method="init" destroy-method="close">
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />

        <!-- 初始化连接大小 -->
        <property name="initialSize" value="0" />
        <!-- 连接池最大使用连接数量 -->
        <property name="maxActive" value="20" />

        <!-- 连接池最小空闲 -->
        <property name="minIdle" value="0" />
        <!-- 获取连接最大等待时间 -->
        <property name="maxWait" value="60000" />
        <property name="poolPreparedStatements" value="true" />
        <property name="maxPoolPreparedStatementPerConnectionSize"
            value="33" />
        <!-- 用来检测有效sql -->
        <property name="validationQuery" value="${validationQuery}" />
        <property name="testOnBorrow" value="false" />
        <property name="testOnReturn" value="false" />
        <property name="testWhileIdle" value="true" />
        <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
        <property name="timeBetweenEvictionRunsMillis" value="60000" />
        <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
        <property name="minEvictableIdleTimeMillis" value="25200000" />
        <!-- 打开removeAbandoned功能 -->
        <property name="removeAbandoned" value="true" />
        <!-- 1800秒,也就是30分钟 -->
        <property name="removeAbandonedTimeout" value="1800" />
        <!-- 关闭abanded连接时输出错误日志 -->
        <property name="logAbandoned" value="true" />
        <!-- 监控数据库 -->
        <property name="filters" value="mergeStat" />
    </bean>
    <!-- myBatis文件 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <!-- 添加mybatis的配置 -->
        <property name="configLocation" value="classpath:mybatis.xml"/>
        <!-- 自动扫描entity目录, 省掉Configuration.xml里的手工配置 -->
        <property name="mapperLocations" value="classpath:com/tanrice/dao/impl/*.xml" />
    </bean>

    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.tanrice.dao" />
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
    </bean>

    <!-- 配置事务管理器 -->
    <bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <!-- 配置事物 -->
    <tx:advice id="transactionAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="add*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="append*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="insert*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="save*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="update*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="modify*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="edit*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="delete*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="remove*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="repair" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="delAndRepair" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="get*" propagation="SUPPORTS" isolation="READ_COMMITTED" />
            <tx:method name="find*" propagation="SUPPORTS" isolation="READ_COMMITTED" />
            <tx:method name="load*" propagation="SUPPORTS" isolation="READ_COMMITTED" />
            <tx:method name="search*" propagation="SUPPORTS" isolation="READ_COMMITTED" />
            <tx:method name="datagrid*" propagation="SUPPORTS" isolation="READ_COMMITTED" />
            <tx:method name="*" propagation="SUPPORTS" isolation="READ_COMMITTED" />
        </tx:attributes>
    </tx:advice>

    <!-- Spring aop事务管理 -->
    <aop:config>
        <aop:pointcut id="transactionPointcut" expression="execution(* com.projectname.service..*Impl.*(..))" />
        <aop:advisor pointcut-ref="transactionPointcut" advice-ref="transactionAdvice" />
    </aop:config>
    <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate" scope="prototype">
         <constructor-arg index="0" ref="sqlSessionFactory" /> 
    </bean>
Annotation-based approach
The annotation-based method is to add the @Transactional annotation to the class or method that needs to be used. The advantage of this is to reduce the waste of resources. The disadvantage is that sometimes it is forgotten. Once forgotten, some methods will have no transactions.
    <!-- 配置数据源 使用的是Druid数据源 -->
    <bean name="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
        init-method="init" destroy-method="close">
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />

        <!-- 初始化连接大小 -->
        <property name="initialSize" value="0" />
        <!-- 连接池最大使用连接数量 -->
        <property name="maxActive" value="20" />

        <!-- 连接池最小空闲 -->
        <property name="minIdle" value="0" />
        <!-- 获取连接最大等待时间 -->
        <property name="maxWait" value="60000" />
        <property name="poolPreparedStatements" value="true" />
        <property name="maxPoolPreparedStatementPerConnectionSize"
            value="33" />
        <!-- 用来检测有效sql -->
        <property name="validationQuery" value="${validationQuery}" />
        <property name="testOnBorrow" value="false" />
        <property name="testOnReturn" value="false" />
        <property name="testWhileIdle" value="true" />
        <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
        <property name="timeBetweenEvictionRunsMillis" value="60000" />
        <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
        <property name="minEvictableIdleTimeMillis" value="25200000" />
        <!-- 打开removeAbandoned功能 -->
        <property name="removeAbandoned" value="true" />
        <!-- 1800秒,也就是30分钟 -->
        <property name="removeAbandonedTimeout" value="1800" />
        <!-- 关闭abanded连接时输出错误日志 -->
        <property name="logAbandoned" value="true" />
        <!-- 监控数据库 -->
        <property name="filters" value="mergeStat" />
    </bean>

    <!-- myBatis文件 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <!-- 添加mybatis的配置 -->
        <property name="configLocation" value="classpath:mybatis.xml"/>
        <!-- 自动扫描entity目录, 省掉Configuration.xml里的手工配置 -->
        <property name="mapperLocations" value="classpath:com/tanrice/dao/impl/*.xml" />
    </bean>

    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.tanrice.dao" />
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
    </bean>

    <!-- 配置事务管理器 -->
    <bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>


    <!-- 注解的方式配置事务 -->
    <!-- 在需要的地方配置 -->
    <tx:annotation-driven transaction-manager="transactionManager"  proxy-target-class="true"/>

    <!-- 注解方式配置事物 -->
    <tx:advice id="transactionAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="add*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="append*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="insert*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="save*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="update*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="modify*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="edit*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="delete*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="remove*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="repair" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="delAndRepair" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="get*" propagation="SUPPORTS" isolation="READ_COMMITTED" />
            <tx:method name="find*" propagation="SUPPORTS" isolation="READ_COMMITTED" />
            <tx:method name="load*" propagation="SUPPORTS" isolation="READ_COMMITTED" />
            <tx:method name="search*" propagation="SUPPORTS" isolation="READ_COMMITTED" />
            <tx:method name="datagrid*" propagation="SUPPORTS" isolation="READ_COMMITTED" />
            <tx:method name="*" propagation="SUPPORTS" isolation="READ_COMMITTED" />
        </tx:attributes>
    </tx:advice>
    <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate" scope="prototype">
         <constructor-arg index="0" ref="sqlSessionFactory" /> 
    </bean>
@Service
@Transactional
public class UserTaskServiceImpl implements UserTaskService{
    
    

    @Autowired
    private UserTaskDao userTaskDao;

    @Autowired
    private TaskDao taskDao;

    public boolean updateUserTask(UserTask userTask,int id) {
        boolean flag = false;
        try{
            flag = userTaskDao.saveUserTask(userTask)>0?true:false;
            System.out.println("插入:"+flag);
            flag = taskDao.updateTaskLeftcountById(id)>0?true:false;
            System.out.println("修改:"+flag);
        }catch(Exception e){
            throw new RuntimeException();
        }
        return flag;
    }
}

You can consider using the above two methods yourself. I wrote the comments in the xml configuration file in more detail, so I won’t explain them bit by bit.

test code

The service method has been written. The main thing is to paste the code of mybatis. I deliberately wrote a SQL statement written in mybatis wrong. An error occurred when it was run.

    <update id="updateTaskLeftcountById">
        <!-- leftcount为int类型,所以加'aas'的时候会出错 -->
        update t_tm_task_table set leftcount = leftcount+aas where id = #{id}
    </update>
    @org.junit.Test
    public void testTrx(){
        UserTask userTask = new UserTask();
        userTask.setFullname("wanda");
        userTask.setUserid(10071);
        userTask.setTaskid(10026);
        userTask.setGettime(new Date().getTime());
        userTask.setState(5);

        System.out.println("结果是:"+userTaskService.updateUserTask(userTask, 10026));
    }

Notice

There are several points to note when using transactions.

  • Why not set up transactions on dao layer
    • The most fundamental reason: Because the DAO layer directly operates the database, there is a single responsibility and no need to roll back. Success is success, and failure is failure.
  • transaction isolation level
    • Read uncommitted: what we call dirty reading, two concurrent transactions, "Transaction A: The leader pays salary to singo", "Transaction B: singo queries salary account", transaction B reads the uncommitted data of transaction A data.
    • Read committed: What we call non-repeatable read, two concurrent transactions, "Transaction A: singo consumption", "Transaction B: singo's wife online transfer", transaction A has read the data in advance, transaction B immediately follows The data has been updated and the transaction has been committed. When transaction A reads the data again, the data has changed.
    • Repeatable read Repeatable read: can avoid non-repeatable reads. When singo takes the salary card to consume, once the system starts to read the salary card information (that is, the transaction starts), singo's wife cannot modify the record, that is, singo's wife cannot transfer money at this time. MySQL's default isolation level is Repeatable read.
    • Serializable Serialization: Serializable is the highest transaction isolation level. It is also the most expensive and has very low performance. It is generally rarely used. At this level, transactions are executed sequentially, which not only avoids dirty reads and non-repeatable reads, but also avoids phantoms. read.
    <!-- 注解方式配置事物 -->
    <tx:advice id="transactionAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="add*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="append*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="insert*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="save*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="update*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="modify*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="edit*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="delete*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="remove*" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="repair" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="delAndRepair" propagation="REQUIRED" isolation="READ_COMMITTED" />
            <tx:method name="get*" propagation="SUPPORTS" isolation="READ_COMMITTED" />
            <tx:method name="find*" propagation="SUPPORTS" isolation="READ_COMMITTED" />
            <tx:method name="load*" propagation="SUPPORTS" isolation="READ_COMMITTED" />
            <tx:method name="search*" propagation="SUPPORTS" isolation="READ_COMMITTED" />
            <tx:method name="datagrid*" propagation="SUPPORTS" isolation="READ_COMMITTED" />
            <tx:method name="*" propagation="SUPPORTS" isolation="READ_COMMITTED" />
        </tx:attributes>
    </tx:advice>

This is the transaction isolation I configured above. <tx:method name="repair" propagation="REQUIRED" isolation="READ_COMMITTED" />Look at the isolation configuration item. This is the configured isolation level.

Guess you like

Origin blog.csdn.net/my_God_sky/article/details/53291138