- 直接在同一个类内部调用 @Transactional 方法
失效示例
@Service
public class SomeService {
@Transactional
public void doSomethingTransactional() {
// 进行一些数据库操作
System.out.println("Doing transactional work...");
}
public void callNonTransactional() {
doSomethingTransactional(); // 事务失效,因为是内部方法调用
}
}
问题:callNonTransactional() 方法直接调用了 doSomethingTransactional(),而没有通过 Spring 容器管理的代理调用,因此事务不会生效。
成功示例
@Service
public class SomeService {
@Autowired
private SomeService someService; // 依赖注入
@Transactional
public void doSomethingTransactional() {
// 进行一些数据库操作
System.out.println("Doing transactional work...");
}
public void callNonTransactional() {
someService.doSomethingTransactional(); // 通过 Spring 管理的实例调用,事务生效
}
}
解决方法:
将 SomeService 注入到自己类中,并通过依赖注入调用 doSomethingTransactional() 方法,这样 Spring 会通过代理进行方法调用,事务管理就能生效。
- 在非 Spring 管理的类中使用 @Transactional
失效示例:
public class NonSpringManagedClass {
@Transactional
public void doSomething() {
// 进行一些数据库操作
System.out.println("Transactional work in non-Spring managed class...");
}
}
问题:NonSpringManagedClass 不是一个 Spring 管理的 Bean,因此事务注解不会生效。
成功示例
@Component // 让该类成为 Spring 管理的 Bean
public class NonSpringManagedClass {
@Transactional
public void doSomething() {
// 进行一些数据库操作
System.out.println("Transactional work in Spring managed class...");
}
}
解决方法:使用 @Component、@Service 或 @Repository 等注解将类标记为 Spring 管理的 Bean,这样 Spring 会为它创建代理,事务注解才会生效。
- 构造函数上使用 @Transactional
失效示例:
@Service
public class SomeService {
@Transactional
public SomeService() { // 事务失效:构造函数不能使用 @Transactional
System.out.println("Initializing with transactional work...");
}
}
问题:构造函数不能使用 @Transactional 注解,因为事务只能应用于实例方法。
解决示例
@Service
public class SomeService {
@Transactional
public void doTransactionalWork() {
System.out.println("Doing transactional work...");
}
}
- 在事务方法内部捕获异常
失效示例:
@Service
public class SomeService {
@Transactional
public void doSomething() {
try {
// 可能抛出 RuntimeException
throw new RuntimeException("Something went wrong");
} catch (RuntimeException e) {
// 异常被捕获,事务不会回滚
System.out.println("Exception caught, transaction will not rollback.");
}
}
}
问题:捕获并处理了 RuntimeException 异常,导致 Spring 无法触发事务回滚。
@Service
public class SomeService {
@Transactional
public void doSomething() {
// 不捕获异常,直接抛出
throw new RuntimeException("Something went wrong");
}
}
解决方法:
让异常 不被捕获,或者明确让 Spring 了解哪些异常需要回滚(通过 rollbackFor 属性)。
例如,使用 @Transactional(rollbackFor = RuntimeException.class) 来指定哪些异常需要触发事务回滚。
备注:
当方法一parentMethod
调用方法二childMethod
时不同情况说明:
eg: @Transactional(rollbackFor = {Exception.class}, timeout = 2, propagation = Propagation.REQUIRED),@Transactional 注解传播行为 propagation 属性默认值是 Propagation.REQUIRED,即当前存在就加入,当前不存在则创建
情况 |
parentMethod 是否加 @Transactional
|
childMethod 是否加 @Transactional
|
事务行为 | 异常处理 |
---|---|---|---|---|
情况 1 | 否 | 否 | 两个方法都不在事务管理范围内,每个数据库操作独立执行 | 出现异常时,已执行的数据库操作不会回滚 |
情况 2 | 是 | 否 |
parentMethod 处于事务管理范围,childMethod 不在事务管理范围,childMethod 操作不参与 parentMethod 的事务 |
- childMethod 异常,parentMethod 事务不回滚- parentMethod 异常,parentMethod 事务回滚,childMethod 已执行操作不受影响 |
情况 3 | 否 | 是 |
parentMethod 不在事务管理范围,childMethod 处于事务管理范围,childMethod 创建新事务 |
- childMethod 异常,其事务回滚- parentMethod 操作不受影响,因本身不在事务中 |
情况 4 | 是(不指定传播行为) | 是(不指定传播行为) |
parentMethod 开启事务,childMethod 加入 parentMethod 的事务,二者共享事务上下文 |
任何一个方法异常,整个事务回滚 |