@Transactional注解避坑指南
@Transactional 注解的事务传播传播行为
-
@Transactional(propagation = Propagation.REQUIRED) :
如果当前没有事务,就新建一个,如果存在事务,就加入到当前事务中(目前最常用选择,也是@Transactional注解默认的传播行为)。
假设A方法调用B方法(B方法使用 REQUIRED),如果A方法存在事务,B方法则加入到A方法事务当中,如果A方法不存在事务,B方法则自己创建一个事务执行。
-
@Transactional(propagation = Propagation.SUPPORTS) :
如果当前存在事务,则加入当前事务,如果当前不存在事务,则不使用事务,当做普通方法运行。
假设A方法调用B方法(B方法使用 SUPPORTS),如果A方法存在事务,B方法则加入到A方法事务当中,如果A方法不存在事务,B方法则当做普通方法执行。
-
@Transactional(propagation = Propagation.MANDATORY) :
如果当前存在事务,则加入当前事务,如果当前不存在事务,则方法抛出异常。
假设A方法调用B方法(B方法使用 MANDATORY),如果A方法存在事务,B方法则加入到A方法事务当中,如果A方法不存在事务,B方法则抛出异常。
-
@Transactional(propagation = Propagation.REQUIRES_NEW) :
如果当前有事务存在,则挂起当前事务,创建一个新的事务。两个事务相互之间不影响。
假设A方法调用B方法(B方法使用 REQUIRES_NEW),B方法会单独创建一个事务,执行完该方法后直接提交,后续A方法出现异常,B方法不进行回滚。
-
@Transactional(propagation = Propagation.NOT_SUPPORTED) :
以非事务的方式运行,如果当前存在事务,则挂起当前事务。
假设A方法调用B方法(B方法使用 NOT_SUPPORTED),B方法则以非事务的方式运行,A方法异常时,B方法则不会进行回滚。
-
@Transactional(propagation = Propagation.NEVER) :
以非事务方式运行,如果当前有事务存在,则抛出异常。
假设A方法调用B方法(B方法使用 NEVER),如果A方法存在事务,则B方法抛出异常。
-
@Transactional(propagation = Propagation.NESTED) :
如果当前存在事务,则嵌套在事务内执行,如果当前没有事务,则创建一个事务执行。
假设A方法调用B方法(B方法使用 NESTED),如果A方法存在事务,则B方法事务会嵌套进A方法事务中,与父事务是相互相依的,父事务提交,则子事务也提交,子事务失败回滚,则父事务也回滚。
@Transactional 注解隔离级别
@Transactional注解的隔离级别对应mysql事务的隔离级别,这里只对隔离级别做个简单的介绍,详情可以访问 数据库事务及隔离级别详解
-
@Transactional( Isolation = Isolation.DEFAULT);
Isolation.DEFAULT为数据源的默认隔离级别 也是@Transactional注解默认的隔离级别
-
@Transactional( isolation = Isolation.READ_UNCOMMITTED);
Isolation.READ_UNCOMMITTED: 读未提交 ,以操作同一行数据为前提,读事务允许其他读事务和写事务,未提交的写事务禁止其他写事务(但允许其他读事务)。此隔离级别可以防止更新丢失,但不能防止脏读、不可重复读、幻读。此隔离级别可以通过“排他写锁”实现。
-
@Transactional( isolation = iIsolation.READ_COMMITTED);
iIsolation.READ_COMMITTED:读已提交 ,以操作同一行数据为前提,读事务允许其他读事务和写事务,未提交的写事务禁止其他读事务和写事务。此隔离级别可以防止更新丢失、脏读,但不能防止不可重复读、幻读。此隔离级别可以通过“瞬间共享读锁”和“排他写锁”实现。
-
@Transactional( isolation = iIsolation.REPEATABLE_READ);
iIsolation.REPEATABLE_READ: 可重复读 ,以操作同一行数据为前提,读事务禁止其他写事务(但允许其他读事务),未提交的写事务禁止其他读事务和写事务。此隔离级别可以防止更新丢失、脏读、不可重复读,但不能防止幻读。此隔离级别可以通过“共享读锁”和“排他写锁”实现。
-
@Transactional( isolation = iIsolation.SERIALIZABLE);
iIsolation.SERIALIZABLE: 序列化 ,提供严格的事务隔离。它要求事务序列化执行,事务只能一个接着一个地执行,不能并发执行。此隔离级别可以防止更新丢失、脏读、不可重复读、幻读。如果仅仅通过“行级锁”是无法实现事务序列化的,必须通过其他机制保证新插入的数据不会被刚执行查询操作的事务访问到。
(隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大,这就需要根据场景在安全与性能之间做出取舍,安全性越高,效率越低,安全性越低,效率越高)
@Transactional 注解失效
-
当@Transactional 应用到非public修饰的方法上,因为@Transactional 是通过AOP的动态代理来做的,spring事务自己在调用动态代理之前,已经对非public方法过滤了。所以 ** @Transactional 注解应用到非public方法上不会生效** 。
-
非直接调用,而是调用内部方法,会导致@Transactional 失效,因为动态代理代理不到它。
-
当@Transactional 注解属性 propagation 值设置错误时可能会导致失效,如Propagation.SUPPORTS,父类方法没有事务或压根没有父类方法时,该方法会以普通方法运行。
-
当@Transactional 注解属性 rollbackFor 值设置错误时可能会导致失效,Transactional 默认支持的异常是Error和RuntimeException,默认只有出现Error或RuntimeException才支持回滚。如果其他时候需要回滚可以设置支持回滚的异常,或直接设置为 rollbackFor = Exception.class (下文异常图 默认蓝色部支持回滚)
-
当手动catch 异常,也会导致@Transactional 失效,非要catch一定要记得抛处 throw new RuntimeException();
-
当数据库存设置的储引擎不支持事务的时候,@Transactional注解自然也无法生效。在mySql中,支出事务的存储引擎有innodb和bdb,其中innodb为mySql默认的存储引擎,也是最常用的存储引擎。(关于mySql数据库常用的存储引擎可以访问 mySql常用存储引擎 了解更多)。