spring--事务失效原因

✅ 一、事务失效的常见原因及对应场景


1. 方法不是 public

✅ 原因:

Spring AOP 默认使用基于代理的方式(JDK 或 CGLIB),只能拦截 public 方法。

❌ 错误示例:
@Transactional
void saveData() {  // 非 public,事务无效
    ...
}
✅ 正确写法:
@Transactional
public void saveData() {
    ...
}

2. 同类中方法调用,导致自调用(Self Invocation)相当于this.

✅ 原因:

Spring 的事务是通过代理实现的,内部方法调用绕过了代理对象,事务不会生效。

❌ 错误示例:
@Service
public class UserService {
    @Transactional
    public void outerMethod() {
        //相当于this. innerMethod();
        innerMethod();  // ❌ 直接调用内部方法,事务不生效
    }

    @Transactional
    public void innerMethod() {
        ...
    }
}
✅ 正确做法:
  • 方法调用通过代理对象:

@Autowired
private UserService userServiceProxy;

public void outerMethod() {
    userServiceProxy.innerMethod(); // ✅ 调用代理对象的方法,事务生效
}

  • 把内部方法移到别的类中,由 Spring 注入。


3. 抛出的是被捕获的异常

✅ 原因:

默认事务只在 未捕获的运行时异常(RuntimeException)或 Error 时才回滚。

❌ 错误示例:
@Transactional
public void saveData() {
    try {
        ...
        throw new RuntimeException("错误");
    } catch (Exception e) {
        // 异常被捕获,事务不会回滚
    }
}
✅ 正确做法:
  • 手动回滚:

catch (Exception e) {
    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}

4. 抛出的不是 RuntimeException 类型

✅ 原因:

默认只回滚 RuntimeExceptionError。若抛出的是 Exception(受检异常),事务不会回滚。

❌ 错误示例:
@Transactional
public void saveData() throws Exception {
    throw new Exception("checked exception");  // ❌ 不会回滚
}
✅ 正确做法:
@Transactional(rollbackFor = Exception.class)
public void saveData() throws Exception {
    throw new Exception("checked exception");  // ✅ 回滚
}

5. 数据库不支持事务或操作不在事务范围内

常见情况:
  • 操作的是非 InnoDB 引擎(MyISAM)

  • DDL 操作(create/drop)在某些数据库中不受事务控制

  • 多线程异步任务里执行数据库操作


6. 多线程或异步方法中的事务不生效

✅ 原因:

多线程或异步执行的方法不会在原事务上下文中执行。

❌ 错误示例:
@Transactional
public void outer() {
    new Thread(() -> save());  // ❌ 新线程,不受事务控制
}
✅ 正确做法:
  • 使用 @Async 并确保异步方法在另一个类中。

  • 或者使用消息队列等方案异步解耦。


7. 事务方法被 final 修饰

✅ 原因:

Spring 的代理机制无法代理 final 方法。

❌ 错误示例:
@Transactional
public final void save() {
    ...
}

8. 注解放在接口上

✅ 原因:

Spring 默认只识别实现类的方法上的注解。

❌ 错误示例:
public interface UserService {
    @Transactional
    void save();  // 接口上标注无效
}
✅ 正确做法:
public class UserServiceImpl implements UserService {
    @Transactional
    public void save() {
        ...
    }
}

9. 配置未生效或使用场景不完整

  • 没有启用事务管理器(@EnableTransactionManagement

  • 多数据源没有配置事务管理器

  • 没有被 Spring 管理的 Bean


✅ 二、事务失效的排查建议

  1. 方法是否是 public

  2. 是否是自调用

  3. 是否正确抛出了 RuntimeException

  4. 事务注解是否写在了实现类上

  5. 数据源和事务管理器是否配置正确

  6. 是否使用了线程池或异步导致线程上下文丢失

  7. 是否误用了 finalstatic 修饰方法

  8. 是否开启了事务管理 @EnableTransactionManagement


✅ 三、总结表格(事务失效排查清单)

场景 是否生效 解决方案
方法非 public 改为 public
方法自调用 用代理调用或拆分类
异常被捕获 手动回滚或抛出
抛出 checked 异常 rollbackFor = Exception.class
多线程调用 不共享上下文,改为 MQ 或异步类
使用 final 修饰方法 去掉 final
注解写在接口上 改写在实现类
未启用事务注解 加上 @EnableTransactionManagement

你可能感兴趣的:(数据库,sql,java,后端,spring)