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.