@Transactional
是咱们在用Spring时候简直逃不掉的一个注解,该注解次要用来申明事务。它的实现原理是通过Spring AOP在注解润饰办法的前后织入事务管理的实现语句,所以开发者只须要通过一个注解就能代替一系列繁琐的事务开始、事务敞开等重复性的编码工作。
编码方式的确简略了,但也因为暗藏了直观的实现逻辑,一些谬误的编码方法可能会让@Transactional
注解生效,达不到事务的作用。最间接的体现就是:办法执行过程中抛出了异样,但事务没有回滚,最终导致了脏数据的产生。
之前我在博客上也写过一篇乏味的探讨我来出个题:这个事务会不会回滚?,过后很多人都给出了规范的谬误答案,如果没看过的小伙伴无妨进去挑战一下?
尽管之前探讨了一些非凡状况,但还是始终有小伙伴会邮件、微信群里问一些对于事务生效的问题。次要还是@Transactional
申明事务生效的状况真的是多种多样!所以,明天写一篇总结一下,如果下次再碰到,那就关上这片文章,一个个顺下来看,是不是哪里写错了。当然可能这里还会有脱漏,所以如果你有其余谬误案例,也能够通知我,我会继续整顿到这篇文章里。
1. 在同一个类中调用
谬误案例:
public class A { public void methodA() { methodB(); // 其余操作 } @Transactional public void methodB() { // 写数据库操作 } }
这类谬误实用于所有基于Spring AOP实现的注解,比方:《应用@Async实现异步调用》中提到的@Async
注解,《应用@Scheduled实现定时工作》中提到的@Scheduled
注解,还有Spring缓存注解的应用解中提到的@Cacheable
注解等。
解决这个问题的办法比较简单,还是正当布局好档次关系即可,比方这样:
@Service@AllArgsConstructorpublic class A { private B b; public void methodA() { b.methodB(); // 其余操作 }}@Servicepublic class B { @Transactional public void methodB() { // 写数据库操作 } }
留神:这里A类用了结构器注入B的实现(为什么没用@Autowrire
,能够看看前几天分享的这篇什么时候不要用@Autowired注入),构造函数用Lombok的@AllArgsConstructor
生成(这个不相熟的话能够看看之前这篇Lombok:让JAVA代码更优雅)。
2. @Transactional润饰办法不是public
谬误案例:
public class TransactionalMistake { @Transactional private void method() { // 写数据库操作 } }
这也是基于Spring AOP实现的注解所要满足的要求。这个最简略,很好了解,也很直观,就不具体开展了。间接把办法拜访类型改成public
即可。
3. 不同的数据源
谬误案例:
public class TransactionalMistake { @Transactional public void createOrder(Order order) { orderRepo1.save(order); orderRepo2.save(order); }}
有的时候,咱们一个操作可能会同时写多个数据源,比方下面这个例子里的orderRepo1
和orderRepo2
是连贯的两个不同数据源。默认状况下,这种跨数据源的事务是不会胜利的。
如果要在多个数据源之间实现事务,那么能够引入JTA,具体如何做的话能够看看之前的这篇分享《应用JTA实现多数据源的事务管理》
4. 回滚异样配置不正确
默认状况下,仅对RuntimeException
和Error
进行回滚。如果不是的它们及它们的子孙异样的话,就不会回滚。
所以,在自定义异样的时候,要做好适当的布局,如果要影响事务回滚,能够定义为RuntimeException
的子类;如果不是RuntimeException
,但也心愿触发回滚,那么能够应用rollbackFor
属性来指定要回滚的异样。
public class TransactionalMistake { @Transactional(rollbackFor = XXXException.class) public void method() throws XXXException { }}
5. 数据库引擎不反对事务
这个来源于一个读者反馈的例子,代码跟我的案例一摸一样,我这边是好的,但他就是不回滚。
起初排查进去是因为漏了一个要害属性的配置:
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
这里的spring.jpa.database-platform
配置次要用来设置hibernate应用的方言。这里顺便采纳了MySQL5InnoDBDialect,次要为了保障在应用Spring Data JPA时候,Hibernate主动创立表的时候应用InnoDB存储引擎,不然就会以默认存储引擎MyISAM来建表,而MyISAM存储引擎是没有事务的。
如果你的事务没有失效,那么能够看看创立的表,是不是应用了MyISAM存储引擎,如果是的话,那就是这个起因了!
小结
如果你看到最初,发现还有其余状况还没有囊括其中,欢送通知咱们哟,咱们会继续更新这篇文章!以帮忙碰到此类问题的读者。
好了,明天的学习就到这里!如果您学习过程中如遇艰难?能够退出咱们超高品质的Spring技术交换群,参加交换与探讨,更好的学习与提高!
欢送关注我的公众号:程序猿DD,分享其余中央看不到的常识与思考