自调用问题(Self-Invocation)
如果一个事务性方法在同一个类中被另一个非事务性方法调用,@Transactional
注解将不会生效。这是因为Spring的事务管理是通过代理实现的,自调用不会通过代理,因此不会触发事务处理。
示例
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Transactional
public void transactionalMethod() {
// some transactional code
}
public void nonTransactionalMethod() {
// self-invocation
transactionalMethod(); // @Transactional will not work here
}
}
解决方法
将调用方法和被调用方法放在不同的类中,或者使用AOP的方式来解决自调用问题。
非公共方法(Non-Public Methods)
@Transactional
注解只会应用于公共方法。如果你在私有、受保护或包可见的方法上使用@Transactional
注解,它将不会生效。
示例
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Transactional
private void transactionalMethod() {
// This will not work as it's a private method
}
}
解决方法
确保事务性方法是公共的。
没有使用Spring管理的代理
Spring的事务管理是通过AOP代理实现的。如果事务性对象没有通过Spring管理(例如,通过new
关键字手动创建),那么@Transactional
注解将不会生效。
示例
public class Main {
public static void main(String[] args) {
// Direct instantiation, no Spring proxy
UserService userService = new UserService();
userService.transactionalMethod(); // @Transactional will not work
}
}
解决方法
确保所有事务性对象都是由Spring容器管理的。
事务管理器未正确配置
如果没有正确配置事务管理器,或者配置的事务管理器与实际的数据源不匹配,@Transactional
注解也可能会失效。
示例
@Configuration
@EnableTransactionManagement
public class AppConfig {
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
解决方法
确保事务管理器配置正确,并与应用程序使用的数据源匹配。
事务传播行为设置不当
有时候,事务传播行为的设置会导致事务不按预期执行。例如,如果传播行为设置为Propagation.NOT_SUPPORTED
,事务将会被挂起,导致@Transactional
注解失效。
示例
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void nonTransactionalMethod() {
// This method will not run in a transactional context
}
解决方法
根据业务需求正确设置传播行为。
异常类型不匹配
默认情况下,Spring只在遇到未检查异常(继承自RuntimeException
)时回滚事务。如果方法抛出的是检查异常(继承自Exception
),事务不会自动回滚,除非在@Transactional
注解中明确指定。
示例
@Transactional
public void someMethod() throws IOException {
throw new IOException("Checked exception"); // Transaction will not be rolled back
}
解决方法
在@Transactional
注解中指定rollbackFor
或rollbackForClassName
属性:
@Transactional(rollbackFor = Exception.class)
public void someMethod() throws IOException {
throw new IOException("Checked exception"); // Transaction will be rolled back
}
多线程环境
如果在多线程环境中使用@Transactional
注解,Spring事务可能会失效,因为每个线程都有自己的事务上下文。
细节参考:多线程场景下谨慎使用@Transactional注解,你不信我也没办法_@transactional 多线程-CSDN博客
示例
@Service
public class UserService {
@Transactional
public void transactionalMethod() {
new Thread(() -> {
// This code will not run in a transactional context
// since it's executed in a different thread
}).start();
}
}
解决方法
1. 避免在事务性方法中使用多线程,
2. 手动管理好各个线程的事务边界
ps: 声明式事务其实有个弊端,它提交事务的时机是在方法执行完成后的,所以,锁是方法执行完释放,事务也是方法执行完才提交,那问题就出在锁刚刚释放,第二个线程立马拿到锁入栈.
改成加锁并且手动控制事务流程
结论
预防办法:确保方法是公共的,通过Spring容器管理对象,正确配置事务管理器和传播行为,并处理好异常类型和多线程环境。