@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);    }}

有的时候,咱们一个操作可能会同时写多个数据源,比方下面这个例子里的orderRepo1orderRepo2是连贯的两个不同数据源。默认状况下,这种跨数据源的事务是不会胜利的。

如果要在多个数据源之间实现事务,那么能够引入JTA,具体如何做的话能够看看之前的这篇分享《应用JTA实现多数据源的事务管理》

4. 回滚异样配置不正确

默认状况下,仅对RuntimeExceptionError进行回滚。如果不是的它们及它们的子孙异样的话,就不会回滚。

所以,在自定义异样的时候,要做好适当的布局,如果要影响事务回滚,能够定义为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,分享其余中央看不到的常识与思考