@Transactional在设置的时候有以下几个主要属性可以设置,
1.propagation:分别为事务的传播行为;
2.isolation:事务的隔离级别;
3.readOnly读写事务控制;
4.timeout:事务的超时时间;
5.rollbackFor:用于指定能够触发事务回滚的异常类型,可以指定多个异常类型;
6.noRollbackFor:抛出指定的异常类型,不回滚事务,也可以指定多个异常类型;
一 propagation属性是事务的传播行为,一共有七种,默认的传播行为为REQUIRED,具体各个传播行为的解释如下:
①Propagation.REQUIRED:如果当前事务存在,就加入该事务,如果事务不存在,就重新创建一个新的事务
举例:如果方法A和方法B都添加了注解,在默认传播方式下,A方法内调用B方法,两个方法共用的是同一个事务;如果方法A添加了默认传播方式的注解,B方法没有加注解,此时B方法和A方法也是共用同一个事务;
②PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务就以非事务方式执行;
③PROPAGATION_MANDATORY:使用当前事务,如果当前没有事务,就抛出异常;
④PROPAGATION_REQUIRES_NEW:新建事务,如果当前存在事务,就把当前事务挂起;
⑤PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起;
⑥PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,就抛出异常;
⑦PROPAGATION_NESTED:如果当前存在事务,就创建一个子事务执行,如果当前没有事务则执行和PROPAGATION_REQUIRED 类似的操作。
注意:
1.在使用默认隔离级别Propagation.REQUIRED,当A方法调用B方法,两个方法都使用了默认的隔离级别,A方法里面调用的B方法(有异常抛出)做了try-catch操作,此时所有的方法都在同一个事务,当B方法发生异常,这个方法所在的事务就会被Spring设置成rollback状态,因为异常被捕获,所以外部方法执行完commit操作,发现当前事务已经处于rollback状态,所以会回滚所有的操作。
2.在使用隔离级别为PROPAGATION_NESTED,每个NESTED事务执行前会将当前操作保存下来,叫做保存点,如果当前NESTED事务执行失败,则回滚到之前的保存点,保存点使得子事务的回滚不对主事务造成影响,NESTED事务在外部事务提交以后自己才会提交。
总结:
REQUIRES_NEW 最为简单,不管当前有无事务,它都会开启一个全新事务,既不影响外部事务,也不会影响其他内部事务,真正的井水不犯河水,坚定而独立。
REQUIRED 在没有外部事务的情况下,会开启一个独立的新事务,且不会对其他同级事务造成影响;而当存在外部事务的情况下,则会与外部事务同生共死。
NESTED 在没有外部事务的情况下与 REQUIRED 效果相同;而当存在外部事务的情况下,当外部事务回滚时,它会创建一个嵌套事务(子事务)。外部事务回滚时,子事务会跟着回滚;但子事务的回滚不会对外部事务和其他同级事务造成影响。
具体的案例可以查看博客:https://www.51cto.com/article/712850.html
二 isolation:事务的隔离级别
脏读:当一个事务读取另一个事务还没有提交的修改是,产生脏读;
不可重复读:同一个查询在同一个事务中多次执行,由于其他事务所做的修改和删除,每次返回的结果不同,此时会发生非重复读;
幻读:同一个查询在同一个事务中多次执行,由于其他事务所做的插入操作,导致每次返回的结果不同,此时会发生幻读;
有以下5种隔离级别:
1.DEFAULT默认级别
DEFAULT为数据库的默认隔离级别;
2.READ_UNCOMMITTED未授权读取级别
这是最低的隔离级别,一个事务能读取到别的事务未提交的更新数据,很不安全,可能出现丢失更新、脏读、不可重复读、幻读。
3.READ_COMMITTED授权读取级别
以操作同一行数据为前提,读事务允许其他读事务和写事务,未提交的写事务禁止其他读事务和写事务。此隔离级别可以防止更新丢失、脏读,但不能防止不可重复读、幻读。
4.REPEATABLE_READ可重复读取级别
保证同一事务中先后执行的多次查询将返回同一结果,不受其他事务影响。以操作同一行数据为前提,读事务禁止其他写事务,但允许其他读事务,未提交的写事务禁止其他读事务和写事务。此隔离级别可以防止更新丢失、脏读、不可重复读,但不能防止幻读。
5.SERIALIZABLE序列化级别
所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰。提供严格的事务隔离,此隔离级别可以防止更新丢失、脏读、不可重复读、幻读。如果仅仅通过“行级锁”是无法实现事务序列化的,必须通过其他机制保证新插入的数据不会被刚执行查询操作的事务访问到。
三 readOnly读写事务控制
readOnly=true表明所注解的方法或类只是读取数据。
readOnly=false表明所注解的方法或类是增加,删除,修改数据。
四 @Transactional失效场景
1.失效场景一@Transactional 应用在非 public 修饰的方法上
之所以会失效是因为在Spring AOP 代理时,TransactionInterceptor (事务拦截器)在目标方法执行前后进行拦截,Spring代理工厂在启动时,会扫描所有类和方法,并会检查目标方法的修饰符是否为 public,不是 public则不会获取@Transactional 的属性配置信息。
2.失效场景二@Transactional 注解属性 propagation 设置错误
这种失效是由于配置错误,若是错误的配置以下三种 propagation,事务将不会发生回滚。
TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
3.失效场景三@Transactional 注解属性 rollbackFor 设置错误
rollbackFor 可以指定能够触发事务回滚的异常类型。Spring默认抛出了继承自 RuntimeException 的异常或者 Error才回滚事务;其他异常不会触发回滚事务。如果在事务中抛出其他类型的异常,但却期望 Spring 能够回滚事务,就需要指定 rollbackFor属性。
4.失效场景四 同一个类中方法调用,导致@Transactional失效
比如有一个类Test,它的一个方法A,A再调用本类的方法B(不论方法B是用public还是private修饰),但方法A没有声明注解事务,而B方法有。则外部调用方法A之后,方法B的事务是不会起作用的,这是由于Spring AOP代理造成的,因为只有当事务方法被当前类以外的代码调用时,才会由Spring生成的代理对象来管理。
5.失效场景五 异常被你try…catch…了,导致@Transactional失效
Spring容器是根据你方法的抛出的Exception来进行回滚的,如果你代码里自己try…catch 了,并没有抛出异常,就会失效,具体情况还需要具体分析。
6.失效场景六 数据库引擎不支持事务
事务能否生效数据库引擎是支持事务的关键。常用的MySQL数据库默认使用支持事务的innodb引擎。如果数据库引擎切换成不支持事务的myisam,那事务就会失效了。
注意:
作用于类:当把@Transactional 注解放在类上时,表示所有该类的public方法都配置相同的事务属性信息。
作用于方法:当类配置了@Transactional,方法也配置了@Transactional,方法的事务会覆盖类的事务配置信息。
作用于接口:不推荐这种使用方法,因为一旦标注在Interface上并且配置了Spring AOP 使用CGLib动态代理,将会导致@Transactional注解失效