前言
上两篇文章已经详细分析了申明式事务的实现原理,知道了底层原理之后,现在就可以开始使用申明式事务去简化我们的代码了。但是在使用@Transactional注解的时候也会经常遇到一些问题,有些问题不仔细测试观察的话还不容易发现,比如:事务失效,部分数据回滚,导致数据不一致等。这篇文章就来列一下使用@Transactional注解时应该注意哪些问题。
一、@Transactional事务不生效的各种场景
1.1 在同一个类中,有两个添加了@Transactional注解的方法A和B,A方法调用B方法,B方法事务不生效
原因是A方法上面添加了事务的注解,所以是通过AOP生成的代理对象去调用被增强之后的方法,但是方法A调用同一个类中的另外一个添加了注解的B方法则是通过当前对象,也就是this去调用的,this并不是增强过后的代理对象,所以没有被AOP添加事务增强。同理其他的注解也是这样,这种情况也是会导致B方法上面的注解失效,如@Async等。
1.2 在非public方法上面添加@Transactional注解
由于注解是基于AOP动态代理生成代理对象调用增强方法的,如果方法为非public的方法,则无法通过动态代理调用方法和增强方法。
1.3 注解中配置了rollbackFor熟性,但是抛出的异常并不是所配置的属性
@Transactional注解的rollbackFor默认是RuntimeException才会进行回滚,所以当方法抛出Exception的时候是不会触发事务的回滚的。
这个方法并不会让事务回滚,因为这里抛出的是Exception,事务默认是RuntimeException才会进行回滚。所以可以改成@Transactional(rollbackFor = Exception.class)
1.4 数据库引擎设置成了MyISAM
MyISAM引擎是一个适合查询的数据库引擎,它是不支持事务和索引的,所以当引擎为MyISAM的时候,事务是不生效的
1.5 在方法中使用了try catch将异常捕获了
如果方法中使用了try catch捕获了异常,而且异常并没有抛出来,则事务也不会进行回滚
1.6 多线程操作事务
如果主线程需要先执行一些修改数据库的操作,当子线程在进行处理出现异常时,主线程修改的数据则不会回滚。因为需要事务生效前提是操作的是同一个数据库的connect,而多线程的情况下,操作的都不是同一个connect,所以会导致事务不生效。
1.7 使用了特殊的事务传播特性
比如在使用PROPAGATION_REQUIRES_NEW和PROPAGATION_NESTED传播特性之后,在子事务回滚之后父事务是不会回滚的,所以这就可能造成事务没生效的误解。在使用传播特性的时候一定要了解各种传播特性的作用。引入大佬文章《Spring事务的传播特性和隔离级别》
二、申明式事务的其他特性
2.1 @Transactional添加readOnly = true提高只读方法的性能
《@Transactional(readOnly=true) 真的是提高性能的灵丹妙药吗?》
文章大概总结了@Transactional添加readOnly = true属性可以提高只读方法的性能的原理
-
性能改进:只读实体不进行脏检查
-
节省内存:不维护持久状态的快照
-
数据一致性:只读实体的更改不会持久化
-
当我们使用主从或读写副本集(或集群)时,
@Transactional(readOnly = true)
使我们能够连接到只读数据库
但是在日常开发中,我们是不会在一个只读方法上添加@Transactional注解的,但是需要注意的是,当一个非只读方法上面有@Transactional注解,而这个方法调用了只读方法,只读方法也会被事务增强,这个场景才需要在只读方法上添加@Transactional(readOnly=true)
2.2 多线程事务怎么回滚
《支付宝一面:多线程事务怎么回滚?说用 @Transactional 可以回去等通知了!》
2.3 事务的隔离级别相关
《MYSQL事务的隔离性之MVCC》
总结
本文主要是总结神秘式事务 @Transactional在使用需要我们去注意的问题,正确的使用事务是在日常开发中所必须具备的技能,不然可能会造成数据的不准备等问题。所以在使用事务的时候必须先全面的理解它,才能够使用好它。如果有需要补充的,感谢提供宝贵的意见