@Transaction中的属性信息
value :
当在配置文件中配有多个事务管理器时,可用该属性指定选择哪一个事务管理器。java
propagation :
事务的传播行为。所谓事务的传播行为,是指在当前事务开启以前,已经有一个事务上下文存在了的状况(好比说咱们给方法A和方法B分别加上了事务,而后在A中调用了B,这时就须要选择事务传播行为,使A作出期待的应对。数据库
该属性包含的可选项以下 :并发
- REQUIRED(默认):
- 若方法A调用时,上下文中存在事务,则加入当前事务
- 若方法A调用时,上下文中没有事务,则新建一个事务
- 当在方法A调用方法B时,方法A、B将使用同一个事务
- 若是方法B发生异常须要回滚时,将回滚整个事务
- REQUIRED_NEW :
- 对于方法A与B,不管方法调用时是否存在事务,都会开启一个新的事务
- 且若是方法调用时存在事务,当前事务将被延缓
- 若是方法B发生异常,将不会致使方法A的回滚
- NESTED :
- 与REQUIRED_NEW相似,但支持JDBC,不支持JPA或Hibernate
- SUPPORTS :
- 若是方法调用时,上下文中已经开启了事务,就使用这个事务
- 若是方法调用时,上下文中没有事务,就不使用事务
- NOT_SUPPORTS :
- 以非事务的方式运行,若是存在事务,在方法调用到结束阶段事务都将被挂起
- NEVER :
- MANDATORY :
- 强制方法在事务中执行,若是存在事务,则加入当前事务
- 若是不存在事务,则将抛出异常
isolation :
事务的隔离级别。所谓事务的隔离级别,是指若干个并发的事务之间的隔离程度,由低到高依次分别为 :read uncommited(读未提交)、read commited(读提交)、read repeatable(读重复)、serializable(序列化)
;这四个级别能够逐个解决脏读、不可重复读、幻读
这几类问题。spa
该属性包含的可选项以下 :代理
- READ_UNCOMMITED :
- 最低级别的事务隔离级别,它容许另外一个事务能够看到这个事务未提交的数据;可致使脏读,不可重复读,以及幻读
- READ_COMMITED :
- 保证一个事务提交以后才能被另外一个事务读取;可防止脏读,但可能致使不可重复读和幻读
- REPEATABLE_READ :
- 不只能实现 READ_COMMITED 的功能,还能进行以下限制 :当A事务读取了一条数据时,B事务将不容许修改这条记录;阻止脏读和不可重复读,但可能致使幻读
- SERIALIZABLE :
- 开销最大但最可靠的事务隔离级别,事务被处理为顺序执行;除了防止脏读、不可重复读以外,还避免了幻读
- DEFUALT :
- 使用当前数据库的默认隔离级别,如 Oracle、SqlServer 是 READ_COMMITED,MySQL 是 REPEATABLE_READF
其中,脏读、不可重复读、幻读概念以下 :code
- 脏读 :读取了未提交的数据
- 好比说A操做试图将帐户余额修由2000修改成1000,B操做读取了该修改后,试图在原余额的基础上增长1000;在B操做执行期间,A操做发生错误致使回滚,帐户余额又变回了2000;那么,当B提交操做后,帐户余额本该有2000+1000=3000,但因为B读取了错误的数据,即脏数据,此时的帐户余额就仅剩下1000+1000=2000了。
- 不可重复读 :先后屡次读取,数据内容不一致
- 好比说,咱们有一个操做A须要屡次读取帐户余额,在A的第一次读取中,余额为1000,这时另外一个操做B将余额修改成了2000,那么在A的第二次读取中,就会发现余额变为了2000,和第一次读取的数据不同了;系统在同一个操做中没法读取同一个数据,称为不可重复读。
- 幻读 :先后屡次读取,数据总量不一致
- 好比说事务A在执行读取操做时,须要屡次统计数据的总量,前一次查询数据总量后,此时B事务执行了新增数据的操做并提交,这时候A再去读取,就会像产生幻觉同样平白多读出了几条数据,所以称为幻读。
timeout :
事务的过时时间。默认为当前数据库的事务过时时间orm
readonly :
指定当前事务是否为只读事务,默认为false对象
rollbackFor :
指定哪一个或哪些异常发生时,须要引发事务回滚,默认为Throwable的子类事务
noRollBackFor :
指定哪一个或哪些异常发生时,不引发事务回滚it
Spring中注解方式的事务实现机制
在应用系统调用声明 @Transactional 的目标方法时,Spring Framework :
- 默认使用 AOP 代理
- 在代码运行时生成一个代理对象
根据 @Transaction 的属性配置信息,这个代理对象将 :
- 决定该目标方法是否由拦截器 TransactionInterceptor 来拦截
TransactionInterceptor在拦截时,会 :
- 首先在目标方法开始执行以前建立并加入事务
- 而后执行目标方法
- 最后根据执行状况是否出现异常,利用抽象事务管理器 AbstarctPlatformTransactionManager 操做数据源 DataSource 提交或回滚事务。
其余注意事项
@Transaction只有应用到public方法才能有效
这是由于在使用 Spring AOP 代理时,Spring在调用 TransactionInterceptor 拦截器对方法进行拦截以前,CglibAopProxy 的一个内部类中的 intercept 方法会间接调用 AbstractFallbackTransactionAttributeSource(Spring 经过这个类获取 @Transaction 注解的事务属性配置属性信息)的 computeTransactionAttribute 方法。
这个方法会检查目标方法的修饰符是否是 public,若是不是,就不会获取 @Transaction 的属性配置信息,最终形成不会使用 TransactionInterceptor 来拦截改目标方法,从而致使事务管理不被进行。
避免 Spring 中的 AOP 自调用问题
在 Spring 的 AOP 代理下,只有当目标方法由外部调用的时候,该目标方法才能由 Spring 生成的代理对象来管理。若同一类中,其余某个没有 @Transaction 注解的方法调用了有 @Transaction 注解的方法,这个有 @Transaction 注解的方法的事务将被忽略,不会发生回滚。
好比以下这种状况,咱们有一个Demo类,类中有两个方法 A 和 B,A 上声明了事务,而 B 上没有时,当 B 调用了 A,A 中的事务将不会生效。
@Server
public class Demo {
public void methodB() {
methodA();
}
@Transaction
public void methodA() {
}
}
复制代码
这是由于,当咱们使用 AOP 进行拦截时,首先会建立一个 Demo 的代理类,这个时候咱们的系统中就会存在两个 Demo 对象了 :一个是目标 Demo 对象
,一个是这个生成的代理 Demo 对象
。若是在代理类的 A 方法中调用代理类的 B 方法,这时候 AOP 拦截是能够生效的,可是,若是在代理类的 A 方法中调用目标类的 B 方法,这时候 AOP 拦截就不会生效了。