乐趣区

关于java:使用-Transactional-时常犯的N种错误

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

}

有的时候,咱们一个操作可能会同时写多个数据源,比方下面这个例子里的 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,分享其余中央看不到的常识与思考

退出移动版