@Transactional 失效的8种场景

1. 方法不是 public修饰

Spring AOP 仅对 public 方法生成代理,非 public 方法(private、protected、default)的 @Transactional 会被忽略,事务不生效。

// 错误示例:private方法的事务无效
@Transactional
private void saveData() { ... }

2. 未开启事务管理

需通过 @EnableTransactionManagement 注解开启 Spring 事务支持(Spring Boot 项目会自动开启,非 Boot 项目需手动配置)。若未开启,@Transactional 注解完全不生效。

3. 事务传播行为配置错误

某些传播行为会导致事务不生效,例如:

● Propagation.NOT_SUPPORTED:以非事务方式执行,若当前存在事务则挂起。

● Propagation.NEVER:不允许在事务中执行,若当前存在事务则抛异常。

// 错误示例:此方法不会在事务中执行
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void saveData() { ... }

4. 异常被捕获且未重新抛出

事务默认仅在未捕获的 unchecked 异常(如 RuntimeException)时回滚。若方法内部捕获了异常且未重新抛出,事务会认为执行成功而提交,导致 “看似失效”。

// 错误示例:异常被捕获,事务不会回滚
@Transactional
public void saveData() {
    try {
        // 数据库操作
        throw new RuntimeException("出错了");
    } catch (Exception e) {
        // 仅捕获异常未抛出,事务提交
    }
}

5. 异常类型不匹配

默认情况下,事务仅对 RuntimeException 和 Error 回滚。若抛出 checked 异常(如 IOException),且未通过 rollbackFor 指定,事务不会回滚。

// 错误示例:抛出IOException(checked异常),事务不回滚
@Transactional
public void saveData() throws IOException {
    throw new IOException("IO错误"); 
}
 
// 正确示例:指定rollbackFor包含checked异常
@Transactional(rollbackFor = IOException.class)
public void saveData() throws IOException { ... }

6. 数据源未配置事务管理器

Spring 事务依赖 PlatformTransactionManager(如 DataSourceTransactionManager)。若未配置事务管理器(或配置错误),@Transactional 无法生效。

// 正确配置示例(Spring Boot自动配置,非Boot需手动)
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
    return new DataSourceTransactionManager(dataSource);
}

7. 数据库本身不支持事务

若使用的数据库存储引擎不支持事务(如 MySQL 的 MyISAM 引擎),即使配置了 @Transactional,事务也不会生效。需将引擎改为 InnoDB。

8.同一类中调用 @Transactional方法失效

Spring 的事务管理基于 AOP 动态代理

● 当外部调用被 @Transactional 标注的方法时,Spring 会通过代理对象拦截调用,在方法执行前后开启、提交 / 回滚事务。

● 但如果是类内部方法调用(例如 A.method1() 调用同一个类的 A.method2(),且 method2 被 @Transactional 标注),此时调用的是目标对象本身(而非代理对象),AOP 拦截器无法生效,导致事务注解失效。

@Service
public class UserService {
 
    // 内部调用带@Transactional的方法
    public void createUser() {
        // 此处调用的是目标对象的saveUser(),而非代理对象,事务失效
        this.saveUser(); 
    }
 
    @Transactional
    public void saveUser() {
        // 数据库操作(事务不生效)
    }
}

如何避免同一类内部调用导致的事务失效?

若必须在同一类中调用事务方法,可通过以下方式让事务生效:

1. 自注入代理对象(需开启 @EnableAspectJAutoProxy(exposeProxy = true)):

@Service
public class UserService {
    // 注入自身代理对象
    @Autowired
    private UserService self;
 
    public void createUser() {
        // 调用代理对象的事务方法
        self.saveUser(); 
    }
 
    @Transactional
    public void saveUser() { ... }
}

2. 通过 AopContext 获取代理对象

public void createUser() {
    // 获取当前类的代理对象
    UserService proxy = (UserService) AopContext.currentProxy();
    proxy.saveUser(); // 事务生效
}
© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
冯晓小的头像 - 鹿快
评论 共1条

请登录后发表评论