当前位置: 首页 > 后端技术 > Java

使用@Transactional时常犯的N种错误

时间:2023-04-01 22:45:01 Java

@Transactional是我们在使用Spring时难以逃避的一个注解。该注解主要用于声明交易。其实现原理是利用SpringAOP在注解修饰方式前后编织事务管理实现语句,开发者只需使用一个注解即可代替事务启动、事务关闭等一系列繁琐的重复编码工作。编码方式确实简单,但也因为隐藏了直观的实现逻辑,一些错误的编码方式可能会使@Transactional注解失效,无法实现交易的功能。最直接的表现就是:方法执行过程中抛出异常,但事务没有回滚,最终导致脏数据的产生。之前在我的博客上也写了一个有趣的讨论,我想出了一个问题:这个事务会被回滚吗?当时很多人都给出了标准的错误答案。如果你还没有看过,何不进去挑战一下呢?虽然之前讨论过一些特殊情况,但是还是有小伙伴会在邮件和微信群里询问一些交易失败的问题。主要是@Transactional声明了事务失败的情况真的有很多!所以,今天写个总结。下次遇到的话,打开这篇文章,一篇一篇的看,看看有没有什么不对的地方。当然这里可能会有疏漏,如果大家还有其他的错误案例也可以告诉我,我会继续整理到本文中。1、错误情况在同一个类中调用:publicclassA{publicvoidmethodA(){methodB();//其他操作}@TransactionalpublicvoidmethodB(){//写数据库操作}}这种错误适用于所有基于SpringAOP实现的注解,比如:《使用@Async实现异步调用》中提到的@Async注解,@Scheduled《使用@Scheduled实现定时任务》中提到的注解,以及Spring缓存注解的使用中提到的@Cacheable注解等。这个问题的解决方法比较简单,合理规划层级关系即可,例如:@Service@AllArgsConstructorpublicclassA{privateBb;publicvoidmethodA(){b.methodB();//其他操作}}@ServicepublicclassB{@TransactionalpublicvoidmethodB(){//写数据库操作}}注:这里A类使用构造函数注入B的实现(为什么没有使用@Autowrite,你可以看我前几天分享的这个时候不要使用@Autowired注入),构造函数是用Lombok的@AllArgsConstructor生成的(如果对这个不熟悉可以看之前的Lombok:MakeJAVAcodemore优雅的)。2、@Transactional修饰方法不是publicerrorcase:publicclassTransactionalMistake{@Transactionalprivatevoidmethod(){//写数据库操作}}这也是基于SpringAOP实现的注解需求。这个最简单,容易理解,也很直观,就不详细展开了。只需将方法访问类型更改为公共即可。3.不同数据源错误情况:publicclassTransactionalMistake{@TransactionalpublicvoidcreateOrder(Orderorder){orderRepo1.save(order);orderRepo2.save(订单);}}有时候,我们可能会同时写一个操作多个数据源,比如上面例子中的orderRepo1和orderRepo2,就是连接的两个不同的数据源。默认情况下,跨数据源的此类事务不会成功。如果要实现多个数据源之间的事务,可以引入JTA。详细操作方法可以看之前的分享《使用JTA实现多数据源的事务管理》4。回滚异常配置不正确默认只有RuntimeException和Error回滚。如果他们和他们的后代没有异常,就不会回滚。因此,在自定义异常时,一定要做好规划。如果要影响事务回滚,可以定义为RuntimeException的子类;如果不是RuntimeException,但又想触发回滚,可以使用rollbackFor属性指定回滚异常。publicclassTransactionalMistake{@Transactional(rollbackFor=XXXException.class)publicvoidmethod()throwsXXXException{}}5.数据库引擎不支持事务。这是一个读者反馈的例子。代码和我的完全一样。边缘很好,但他就是不回滚。后来发现漏了一个关键属性的配置:spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect这里的spring.jpa.database-platform配置主要是用来设置使用的方言冬眠。这里特意使用MySQL5InnoDBDialect,主要是为了保证在使用SpringDataJPA时,Hibernate在自动建表时使用InnoDB存储引擎,否则会使用默认存储引擎MyISAM建表,MyISAM存储引擎没有事务。如果你的事务没有生效,你可以检查创建的表是否使用了MyISAM存储引擎。如果是这样,那就是原因!小结如果你读到最后发现还有其他没有涉及到的情况,请告诉我们,我们会持续更新本文!以帮助遇到此类问题的读者。好了,今天的学习就到这里!如果你在学习过程中遇到困难?可以加入我们超优质的Spring技术交流群,参与交流讨论,更好的学习进步!欢迎关注我的公众号:程序员DD,分享别处看不到的知识和思考