@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
@AllArgsConstructor
public class A {
private B b;
public void methodA() {b.methodB();
// 其余操作
}
}
@Service
public 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,分享其余中央看不到的常识与思考