Spring @Transactional(REQUIRES_NEW)事务异常求助:内层事务未提交
我来帮你拆解这个问题——从你的描述和日志来看,核心是Spring的JTA事务管理和JBoss容器的数据源事务控制没协同好,导致内层的REQUIRES_NEW事务没有真正独立提交,最终跟着外层事务一起回滚了。先理清楚你的场景和配置:
你的场景与问题
- 外层Manager方法标注了
@Transactional,调用了另一个注入的Manager中标记@Transactional(propagation = REQUIRES_NEW)的方法 - 外层事务抛出
MyValidationException(RuntimeException子类)回滚时,你预期内层事务的修改会持久化,但实际完全没提交 - 日志明确显示:
JtaTransactionManager - 发起事务提交,但JDBC驱动未调用commit()方法,只有外层回滚时才触发了JDBC的rollback操作
环境配置
- 容器:JBoss EAP 7
- 框架:Spring 5.1
- 数据源:standalone.xml中配置ojdbc数据源,
jta=true;通过JNDI注入WildFlyDataSource,手动设置autoCommit = false
关键日志片段
11:10:23,782 INFO [stdout] (default task-1) DEBUG JtaTransactionManager - Initiating transaction commit 11:10:23,784 INFO [stdout] (default task-1) TRACE JtaTransactionManager - Triggering afterCommit synchronization 11:10:23,786 INFO [stdout] (default task-1) TRACE JtaTransactionManager - Triggering afterCompletion synchronization >>>此处预期内层事务已提交<<< 11:10:23,787 INFO [stdout] (default task-1) DEBUG JtaTransactionManager - Resuming suspended transaction after completion of inner transaction ... 11:10:23,815 INFO [stdout] (default task-1) DEBUG JtaTransactionManager - Initiating transaction rollback
核心原因分析
JTA数据源的事务控制权冲突
当你配置了jta=true的容器数据源时,连接的事务归属是由JBoss的Arjuna事务管理器控制的,Spring的JtaTransactionManager只是代理容器的事务服务。如果内层的REQUIRES_NEW没有正确触发容器创建新的JTA事务上下文,内层操作就依然绑定在外层事务的连接上,最终被外层回滚带偏。autoCommit=false的干扰
JTA数据源的事务提交/回滚完全由容器事务管理器接管,你手动设置autoCommit=false会打乱容器的事务上下文管理逻辑,导致Spring的事务同步无法正确触发JDBC的commit操作。事务代理生效问题(需排查)
虽然你说调用的是注入的第二个Manager,但还是要确认:是不是通过Spring代理对象调用的?如果是同一类内的方法调用(比如外层方法和内层方法在同一个bean里),Spring的动态代理不会生效,REQUIRES_NEW会被忽略。
解决方案
1. 让Spring正确集成容器的JTA事务管理器
不要手动配置数据源的事务管理,让Spring自动检测JBoss的Arjuna事务管理器:
@Bean public PlatformTransactionManager transactionManager() { JtaTransactionManager txManager = new JtaTransactionManager(); txManager.setAutodetectTransactionManager(true); txManager.setAutodetectUserTransaction(true); return txManager; }
这样Spring就会完全委托容器来管理事务上下文,包括REQUIRES_NEW的新事务创建。
2. 修正数据源配置
在JBoss的standalone.xml中,确保数据源jta=true,并且移除autoCommit的手动配置(容器会自动管理):
<datasource jta="true" jndi-name="java:/jdbc/myDS" pool-name="myDS"> <connection-url>jdbc:oracle:thin:@//your-db-host:1521/your-sid</connection-url> <driver>ojdbc</driver> <security> <user-name>db-user</user-name> <password>db-pass</password> </security> <!-- 删掉任何autoCommit相关配置 --> </datasource>
同时Spring中获取数据源时,不要修改autoCommit属性:
@Bean public DataSource dataSource() throws NamingException { Context ctx = new InitialContext(); return (DataSource) ctx.lookup("java:/jdbc/myDS"); }
3. 确保事务方法通过代理调用
如果内层方法和外层方法在同一个bean里,一定要把内层方法抽离到单独的Service/Manager bean中,然后通过Spring注入后调用,这样动态代理才会生效,REQUIRES_NEW才会触发新事务。
4. 确认异常回滚规则
你的MyValidationException是RuntimeException子类,默认会触发回滚,但如果是自定义Checked Exception,需要在外层事务注解中指定rollbackFor:
@Transactional(rollbackFor = MyValidationException.class) public void outerMethod() throws MyValidationException { // 业务逻辑 }
5. 开启详细日志排查
如果还是有问题,开启JBoss Arjuna的TRACE日志,查看事务的创建、挂起、提交细节:
<logger category="com.arjuna"> <level name="TRACE"/> </logger>
同时保持Spring事务的DEBUG日志,确认内层事务是否真的被挂起并创建了新事务。
内容的提问来源于stack exchange,提问作者pvydrysek

