Spring 内部调用,事务真会失效吗?
先说结论。非Spring代理的内部调用,事务本身不会失效。但@Transactional
注解会因为代理失效而失效。
代码示例
同一个类TransactionUserService
中。
@Transactional
public void a() {
userMapper.insert("A");
b();
throw new RuntimeException("触发回滚");
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void b() {
userMapper.insert("B");
}
结果:A、B记录均回滚。
你的预期是否是:A 回滚,B 提交?
如果是。此时你应该在思考,b()
的事务已失效,为什么b()
还会被回滚?但其实此时b()
的事务并未失效,失效的只是 @Transactional(propagation = Propagation.REQUIRES_NEW)
注解本身。我们接着往下验证。
通过事务信息验证
在 a()
和 b()
中使用TransactionSynchronizationManager.*getResourceMap*()
打印事务信息:
// a() 中打印
System.out.println(TransactionSynchronizationManager.getResourceMap());
// b() 中打印
System.out.println(TransactionSynchronizationManager.getResourceMap());
打印结果:
a()
事务信息{{ CreateTime:"2025-02-14 14:10:00", ActiveCount:1, PoolingCount:0, CreateCount:1, DestroyCount:0, CloseCount:0, ConnectCount:1, Connections:[ ] }=org.springframework.jdbc.datasource.ConnectionHolder@4f41cc10}
b()
事务信息{org.apache.ibatis.session.defaults.DefaultSqlSessionFactory@3ffd6f44=org.mybatis.spring.SqlSessionHolder@48585aba, { CreateTime:"2025-02-14 14:10:00", ActiveCount:1, PoolingCount:0, CreateCount:1, DestroyCount:0, CloseCount:0, ConnectCount:1, Connections:[ ] }=org.springframework.jdbc.datasource.ConnectionHolder@4f41cc10}
发现了吗?a()
、b()
的 ConnectionHolder
对象的内存地址一模一样:@4f41cc10
。
为什么?我们首先理清楚两点:
- 事务的本质
Spring事务基于数据库连接,通过
ThreadLocal
将连接绑定到当前线程,同一线程内的操作默认共享事务。 - AOP代理机制
事务的传播行为(如
REQUIRES_NEW
)依赖代理对象拦截方法调用。内部直接调用会绕过代理,导致传播行为失效!
事务存在 ≠ 传播行为生效。所以说内部调用时,即使@Transactional
失效,该方法仍共享父方法事务。
最后,而要使@Transactional(propagation = Propagation.REQUIRES_NEW)
生效,只需简单改为由 spring代理的bean对象 调用内部方法即可。
applicationContext.getBean(TransactionUserService.class).b();
Comments
Post a Comment