在业务的开发中难免会使用到事务。
事务的ACID
- A(Atomicity) 原子性 :一个事务的最终状态只能是执行完成或未执行,不存在执行一半行为的情况。
- C(Consistency) 一致性:事务在执行的前后,从一个状态变成另一个状态,总体的数据保持完整性。
- I(Isolation) 隔离性:一个事务执行过程中对另一个事务没有影响,相互独立。并且一个事务在执行是看待另一个事务只能是执行完成或未执行的状态。
- D(Durability) 持久性:一个事务在执行结束后数据的改变是永久的。
在Spring中使用了注解的形式对事务提供了良好的支持和方便的使用。下面是具体的介绍和使用方法。
源码切入
从源码可以看出:
- 注解主要作用于类和方法 当类和方法都标注时,会优先按方法的配置。
- 如果没有声明任何回滚异常,默认遇到RuntimeException触发回滚, 也就是遇到其他Exception时无法回滚。
- 当你使用try catch捕捉异常后,异常将无法被回滚。
参数分析
value / transactionManager
用来标注事务管理器bean名称org.springframework.transaction.PlatformTransactionManager
propagation
配置事务的传播行为。具体有7种存在如下枚举类中:
类型 | 说明 | 适用场景 |
---|---|---|
REQUIRED | 默认类型 当前如果存在事务就加入,若没有新建一个事务 | 一般情况下 |
SUPPORTS | 当前如果存在事务就加入,不存在则以非事务形式运行 | 适合事务可以有可无的情况 |
MANDATORY | 当前如果存在事务就加入,不存在则抛出异常 | 适合必须要在前一个事务中执行的情况 |
REQUIRES_NEW | 新建事务,如果当前存在事务则把当前事务暂停(suspending) | 适合单独需要新建事务的情况 |
NOT_SUPPORTED | 非事务的形式运行,如果当前存在事务把当前事务暂停(suspending) | 适合不需要被事务影响的情况 |
NEVER | 以非事务的形式执行,如果当前存在事务则抛出异常) | 适合必须不在事务中运行,获取异常处理的情况 |
NESTED | 如果当前事务存在,则在嵌套事务内运行 | 这个没怎么明白 |
Isolation
/**
* Use the default isolation level of the underlying datastore.
* All other levels correspond to the JDBC isolation levels.
* @see java.sql.Connection
*/
事务的隔离级别 默认使用的是数据库底层隔离级别。 MySQL的innodb引擎默认支持的是 REPEATABLE-READ(可重复读)
-
Isolation.READ_UNCOMMITTED读取未提交数据(会出现脏读, 一个事务会读取到另一个事务未提交的数据)基本不使用
-
Isolation.READ_COMMITTED读取已提交数据(会出现不可重复读和幻读)
-
Isolation.REPEATABLE_READ可重复读(会出现幻读)
-
Isolation.SERIALIZABLE串行化 最高的级别,确保了事务之间不会产生错误,但性能相对较低。
timeout
事务的超时时间 默认也是使用数据库系统的内置超时时间,超过这个时间事务自动回滚。MySQL中默认超时时间为50s,可以自行设置。
readOnly
只读属性,默认为false。当设置为true时 事务为只读事务,相当于数据库是只读的。用于一个事务的多条查询之间保持数据的正确性,防止在事务操作的过程中,数据被另一个完成的事务修改导致了两次的不一致性。
如图所示 事务1 两次查询,如果要保持一致性,那么事务2的修改数据操作就不能发生。
rollbackFor
需要回滚的异常类型,默认为Java中受检查的异常。
包括RuntimeException 及其子类 和 Error 。
由于业务中常常直接抛出Exception 通常可设置成
@Transactional(rollbackFor = Exception.class)
也可以实现继承RuntimeException的自定义异常,然后
@Transactional(rollbackFor = MyRuntimeException.class)
rollbackForClassName
声明需要回滚的异常类名,String类型,可以不定义也可以定义一个数组
noRollbackFor noRollbackForClassName
不需要回滚的异常类和类名,和上述两个参数使用方法不尽相同。
使用方法
直接在Service层的方法或者类上注释即可。Spring会采用AOP的形式进行事务的实现。
总结
通过这次学习,我们知道了@Transactional注解为我们带来了很多方便,可以对事务进行简单的处理。但是也需要注意可能带来的风险,比如回滚失败的多种情况,可能没有声明回滚类,或者事务传播行为的错误设定。对各个参数的含义都要有深刻的理解,例如各个隔离级别可能造成的错误和性能的影响等。
每一次的学习总结,都是一次进步!