vlambda博客
学习文章列表

阿里1面,Spring事务问题,直接让我懵逼了

文末可领取最近刚整理的,后端必备的 200 本书籍。

阿里面试题

系统中使用 spring 来管理事务,但是发现事务没有起效,你觉得有哪些原因呢?

可能有以下 8 种情况。

情况 1:未启用 Spring 事务管理配置

看一下代码中是否有 @EnableTransactionManagement 注解,且这个注解需要被 spring 处理,这个注解可以启用 Spring 事务功能。

情况 2:方法必须是 public 类型的

检查一下方法是否是 public 类型的,spring 事务只能对 public 方法起效。

情况 3:未配置事务管理器 Bean

spring 内部是依靠事务管理器(TransactionManager)来操作事务的,所以 spring 容器中必须要有事务管理器这个 bean

@Bean
public TransactionManager transactionManager(DataSource dataSource) {
    return new DataSourceTransactionManager(dataSource);
}

情况 4:事务管理器的数据源和代码中操作 db 的数据源不一致

事务方法所用到的事务管理器的数据源和方法内部执行 sql 所用到的数据源必须一致,否则事务会失效。

创建事务管理器的时候,可以指定数据源,如下:

@Bean
public TransactionManager transactionManager(DataSource dataSource) {
    return new DataSourceTransactionManager(dataSource);
}

而通常我们会使用@Transaction 注解,放在目标方法上,添加事务功能,这个注解有个 transactionManager 属性,可以用来指定事务管理器的 bean 名称,当系统中存在多个事务管理器的时候,可以通过这个属性来指定当前方法具体使用哪个事务管理器,需确保方法内部执行 sql 的数据源和事务管理器中用到的是同一个数据源。

情况 5:方法调用问题

比如下面代码,m2 方法上添加了事务功能,但是此时如果外面直接调用 m1 方法,此时 m2 的事务将失效,因为必须通过代理对象直接调用@Transaction 的方法时,事务才会起效,而 m1 方法中是通过 this 来调用 m2 方法的,而 this 是当前对象,并不是代理对象,那么如果想让 m2 方法事务生效,而又不想在 m1 方法添加@Transaction 注解,有什么办法么?欢迎留言

@Component
public class DemoService {
    public void m1(){
        this.m2();
    }

    @Transactional
    public void m2(){
        //执行db操作
    }
}

情况 6:业务方法内部抛出的异常类型和事务回滚的异常类型不匹配

@Transactional 注解内部有个 rollbackFor 属性,用来指定事务回滚的异常类型,当方法抛出的异常类型和 rollbackFor 类型匹配的时候,事务才能够回滚,而 rollbackFor 的默认值是 RuntimeException 和 Error,如果方法内部抛出的异常类型不匹配,事务是无法回滚的,会让您失望的,所以建议大家使用的时候请务必设置下这个属性的值。

@Transactional(rollbackFor = {异常类型列表})

情况 7:业务代码将异常生吞了

spring 是依靠监听业务方法异常,来决定是回滚事务还是提交事务的,比如下面代码,程序将异常吞了,此时事务是无法回滚的,不管任何使用,建议不要吞掉异常。

@Transactional
public void xxx(){
    事务操作1
    try{
        事务操作2,内部抛出了异常,但是被捕获吞掉了
    }catch(Exception e){
    }
}

情况 8:业务代码和 spring 事务未在一个线程中

比如下面代码,在新的线程中去执行 db 操作,此时事务是失效的,程序需要确保 db 操作的代码和 spring 事务必须在同一个线程中,否则事务不受 spring 控制。

@Transactional
public void m1() {
    new Thread() {
        一系列事务操作
    }.start();
}

领取后端必备的 200 本书