事务失效场景1:方法非public修饰
原因
Spring事务基于动态代理(AOP)实现,非public方法无法被代理拦截,导致事务失效。
代码示例
java
@Service
public class OrderService {
@Transactional
private void createOrder() { // 非public方法
// 业务逻辑
}
}
解决方案
- 将方法改为
public
修饰。 - 若需限制方法访问权限,可通过编程式事务(
TransactionTemplate
)实现。
事务失效场景2:自调用问题
原因
同类中方法A调用方法B(带@Transactional
),由于代理机制失效,事务不生效。
代码示例
java
@Service
public class UserService {
public void updateUser() {
this.saveUser(); // 自调用
}
@Transactional
public void saveUser() {
// 数据库操作
}
}
解决方案
- 将事务方法拆分到另一个类中,通过注入调用。
- 使用
AopContext.currentProxy()
获取代理对象(需开启exposeProxy
)。
事务失效场景3:异常类型未被捕获
原因
默认仅对RuntimeException
和Error
回滚,若抛出其他异常(如IOException
)且未配置rollbackFor
,事务不会回滚。
代码示例
java
@Transactional
public void processData() throws IOException {
// 抛出IOException
throw new IOException("文件异常");
}
解决方案
-
明确指定回滚异常类型:
java@Transactional(rollbackFor = Exception.class)
事务失效场景4:事务传播行为配置错误
原因
例如REQUIRES_NEW
嵌套使用时,内层事务失败可能不影响外层事务。
代码示例
java
@Transactional(propagation = Propagation.REQUIRED)
public void outerMethod() {
innerMethod(); // 内层事务独立提交
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void innerMethod() {
// 操作失败但outerMethod继续执行
}
解决方案
- 根据业务需求调整传播行为,如改为
REQUIRED
。 - 避免过度嵌套事务。
事务失效场景5:多数据源未指定事务管理器
原因
多数据源环境下未明确指定transactionManager
,导致事务绑定到默认管理器。
代码示例
java
@Transactional // 默认使用primary事务管理器
public void saveToSecondaryDB() {
// 操作secondary数据源
}
解决方案
-
注解中指定事务管理器:
java@Transactional("secondaryTransactionManager")
事务失效场景6:手动捕获异常未抛出
原因
捕获异常后未重新抛出,事务拦截器无法触发回滚。
代码示例
java
@Transactional
public void updateOrder() {
try {
// 数据库操作
} catch (Exception e) {
log.error("错误", e); // 未抛出异常
}
}
解决方案
- 在
catch
块中抛出RuntimeException
。 - 或使用
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()
手动回滚。
事务失效场景7:非事务方法调用事务方法
原因
若父类方法未开启事务,调用子类@Transactional
方法时,代理失效。
代码示例
java
public class BaseService {
public void execute() {
save(); // 事务失效
}
@Transactional
public void save() {}
}
解决方案
- 将事务注解添加到父类方法。
- 避免通过继承层级调用事务方法。
总结
- 检查方法修饰符和代理机制。
- 确保异常类型和传播行为匹配业务需求。
- 多数据源需显式指定事务管理器。
- 优先通过设计规避自调用问题。