针对声明式事务 @Transactional 的使用姿势,只知道正确的使用可能还不够,还得知道什么场景下不生效。
不生效的案例及解决方法
一、方法的可见性:
事务管理器只能拦截 `public
` 方法。如果事务方法是 `private
`、`protected
` 或 `package-private
`,则事务不会生效。
解决方法:确保事务方法是 `public
` 的。
二、方法内部调用(自调用):
如果一个事务性方法内部调用了另一个事务性方法(或者调用自身的其他方法),Spring AOP 不会创建新的事务,因为内部调用不会通过代理对象。
解决方法:通过代理对象调用事务方法。例如,将自调用部分提取到另一个 Spring Bean 中。
三、代理类和直接调用:
如果在同一个类中直接调用带有事务注解的方法,不会经过 Spring 的代理对象,导致事务不生效。
解决方法:使用 Spring 提供的代理对象进行调用。
四、事务管理器配置错误:
如果没有正确配置事务管理器,事务将不会生效。
解决方法:确保正确配置事务管理器。例如,使用 `@EnableTransactionManagement
` 注解并配置 `PlatformTransactionManager
`。
@Configuration
@EnableTransactionManagement
public class AppConfig {
@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory emf) {
return new JpaTransactionManager(emf);
}
}
五、事务传播行为:
默认情况下,Spring 的事务传播行为是 `REQUIRED
`。如果事务已经存在,新的方法将会加入到现有事务中。如果事务传播行为配置不当,可能会导致事务不生效。
解决方法:根据具体情况正确设置事务传播行为。例如:
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void someTransactionalMethod() {
// method implementation
}
六、AOP代理配置:
Spring 的 AOP 代理有两种方式:JDK 动态代理和 CGLIB 代理。如果目标类没有实现接口,默认使用 CGLIB 代理。如果代理方式配置不当,可能会导致事务不生效。
解决方法:检查并配置正确的代理方式。
@EnableTransactionManagement(proxyTargetClass = true) // 使用CGLIB代理
七、异常类型:
事务默认只在运行时异常(`RuntimeException
`)或其子类异常发生时回滚。如果抛出的是检查异常(`Checked Exception
`),事务不会回滚。
解决方法:使用 `@Transactional
` 注解的 `rollbackFor
` 属性指定需要回滚的异常类型。
@Transactional(rollbackFor = Exception.class)
public void someTransactionalMethod() {
// method implementation
}
八、数据库连接问题:
如果数据库连接未正确配置或者在事务方法中连接出现问题,事务可能无法正常提交或回滚。
解决方法:确保数据库连接配置正确,并且在事务方法中处理好连接问题。
九、多线程环境:
如果在多线程环境中使用事务,事务上下文可能无法正确传播到其他线程。
解决方法:确保在单线程环境中使用事务,或者使用适当的事务传播机制来处理多线程环境中的事务。