Spring事务
前言
事务是为了达成某种目的而进行的一系列操作,要么全部成功,要么全部失败。
事务的四大特征
- A(原子性):事务是一个不可分割的工作单位,事务中的所有操作,要么全部成功,要么全部失败。(使用undo log保证)
- C(一致性):事务前后数据的完整性必须保持一致。
- I(隔离性):多个用户并发访问数据库时,数据库为每一个用户开启的事物,不能被其它事务所干扰,多个并发事务之间要相互隔离。
- D(持久性):事务一旦提交,它对数据库中数据的改变就是永久性的,即使数据库服务器宕机也不会对其有任何影响。(使用redo log保证)
一、MySQL事务
mysql> select * from user;
+----+------+
| id | name |
+----+------+
| 1 | li |
+----+------+
1 row in set (0.00 sec)
mysql> start transaction; # 开启事务
Query OK, 0 rows affected (0.00 sec)
mysql> insert into user(id,name) values(2,'zhang');
Query OK, 1 row affected (0.01 sec)
mysql> commit; # 提交事务
Query OK, 0 rows affected (0.36 sec)
mysql> select * from user;
+----+-------+
| id | name |
+----+-------+
| 1 | li |
| 2 | zhang |
+----+-------+
2 rows in set (0.00 sec)
mysql> start transaction; # 开启事务
Query OK, 0 rows affected (0.00 sec)
mysql> insert into user(id,name) values(3,'wang');
Query OK, 1 row affected (0.00 sec)
mysql> rollback; # 回滚事务
Query OK, 0 rows affected (0.19 sec)
mysql> select * from user;
+----+-------+
| id | name |
+----+-------+
| 1 | li |
| 2 | zhang |
+----+-------+
2 rows in set (0.00 sec)
并发条件下,事务之间存在的三个问题
问题 | 描述 |
---|---|
脏读 | 一个事物处理过程中读取到另一个事物未提交的数据 |
可重复读 | 一个事物在执行过程中读取同一条记录的结果前后不一致 |
幻读 | 一个事物读取到另一个事务插入的数据,造成前后记录数目不一致 |
事务的四种隔离级别 | |
隔离级别 | 描述 |
---- | ---- |
read uncommit | 一个事物处理过程中读取到另一个事物未提交的数据 |
read commited | 一个事物在执行过程中读取同一条记录的结果前后不一致 |
repeatable read | 一个事物读取到另一个事务插入的数据,造成前后记录数目不一致 |
serializable | 一个事物读取到另一个事务插入的数据,造成前后记录数目不一致 |
二、JDBC事务
JDBC是一种规范,要使数据库与JAVA应用程序进行通信,那么数据库必须遵守JDBC规范。
try {
conn.setAutoCommit(false); // 关闭自动提交事务,使用手动提交事务
...业务逻辑
conn.commit(); // 提交事务
}catch(Exception e) {
conn.rollback(); // 事务回滚
}finally{
// 关闭连接
}
三、Spring事务
1. 基础使用 @Transactional
<!-- 事务管理器 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 开启注解支持 -->
<tx:annotation-driven transaction-manager="txManager" proxy-target-class="true" />
2. 事务隔离级别
Spring事务中,MySQL默认隔离级别为repeatable read
隔离级别 | 使用方式 |
---|---|
read uncommit | @Transactional(isolation = Isolation.READ_UNCOMMITTED) |
read commited | @Transactional(isolation = Isolation.READ_COMMITTED) |
repeatable read | @Transactional(isolation = Isolation.REPEATABLE_READ) |
serializable | @Transactional(isolation = Isolation.SERIALIZABLE) |
3. 事务传播行为
事务传播行为用来描述由某一个事务传播行为修饰的方法被嵌套进另一个方法的时事务如何传播。通过@Transactional(propagation= Propagation.REQUIRED)
方式使用。
传播行为 | 描述 |
---|---|
required | 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。 |
supports | 支持当前事务,如果当前没有事务,就以非事务方式执行。 |
mandatory | 使用当前的事务,如果当前没有事务,就抛出异常。 |
required_new | 新建事务,如果当前存在事务,把当前事务挂起。 |
not supported | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
never | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
nested | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。 |
4. 8大事务失效问题
- 数据库引擎不支持事务
- 没有被Spring管理
- 非public方法
- 自身调用
- 数据源没有配置事务管理器
- 不支持事务
@Transactional(propagation = Propagation.NOT_SUPPORTED)
- 异常被吃
将异常吃掉并且不抛出RuntimeException
try{}catch() {
// throw new RuntimeException("抛出异常")
}
- 异常类型错误
默认回滚RuntimeException,如果需要回滚其它类型异常,需指定@Transactional(rollbackFor = Exception.class)
,这个配置仅限于 Throwable 异常类及其子类。
try{}catch() {
throw new Exception("抛出异常")
}