Spring 是不是真的使用 REQUIRES_NEW 启动新事务?

Posted

技术标签:

【中文标题】Spring 是不是真的使用 REQUIRES_NEW 启动新事务?【英文标题】:Does Spring actually start a new transaction with REQUIRES_NEW?Spring 是否真的使用 REQUIRES_NEW 启动新事务? 【发布时间】:2014-11-27 08:37:25 【问题描述】:

我的 spring (4.1.1) 应用程序部署在 JBoss-6.10-final 实例上,因此它使用基于容器的事务管理器和数据源。对于消息传递,我使用设置了 XA 队列连接工厂的 TIBCO EMS 8.1。 Java 版本为 1.8.0_20。所有这些都在我的 Ubuntu 14.04 笔记本电脑上运行。

我需要通过 JMS 发送请求,然后等待回复。我调用的 bean 的事务传播设置为 Propagation.REQUIRED,所以我需要在新事务中发送请求,然后等待回复。这意味着请求在单独的 bean 中发送,事务传播设置为 Propagation.REQUIRES_NEW。 它有效,但我收到了来自 JBoss 的令人担忧的警告:

14-10-02 12:06:12,902 警告 [org.jboss.tm.usertx.UserTransactionRegistry] (http-0.0.0.0-8080-1) 通知侦听器时出错 org.jboss.resource.connectionmanager.CachedConnectionManager@1917b4de userTransactionStarted: java.lang.IllegalStateException: 试图 更改事务 TransactionImple in 入伍!在 org.jboss.resource.connectionmanager.TxConnectionManager$TxConnectionEventListener.enlist(TxConnectionManager.java:690) 在 org.jboss.resource.connectionmanager.TxConnectionManager.transactionStarted(TxConnectionManager.java:427) 在 org.jboss.resource.connectionmanager.CachedConnectionManager.userTransactionStarted(CachedConnectionManager.java:350) 在 org.jboss.tm.usertx.UserTransactionRegistry.userTransactionStarted(UserTransactionRegistry.java:119) 在 org.jboss.tm.usertx.client.ServerVMClientUserTransaction.begin(ServerVMClientUserTransaction.java:141) 在 org.springframework.transaction.jta.JtaTransactionManager.doJtaBegin(JtaTransactionManager.java:875) 在 org.springframework.transaction.jta.JtaTransactionManager.doBegin(JtaTransactionManager.java:832) 在 org.springframework.transaction.support.AbstractPlatformTransactionManager.handleExistingTransaction(AbstractPlatformTransactionManager.java:425) 在 org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:349) 在 org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:438) 在 org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:261) 在 org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95) 在 org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) 在 org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207) 在 com.sun.proxy.$Proxy234.request(未知来源) com.izazi.ioriginate.framework.spring.jms.AbstractRequestReply.request(AbstractRequestReply.java:58) 在 com.izazi.ioriginate.service.addressvalidation.AddressValidationServiceImpl.validate(AddressValidationServiceImpl.java:34) 在 sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 在 sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 在 sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 在 java.lang.reflect.Method.invoke(Method.java:483) 在 org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317) 在 org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) 在 org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) 在 org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:98) 在 org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:266) 在 org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95) 在 org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) 在 org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207) 在 com.sun.proxy.$Proxy235.validate(Unknown Source) 在 com.izazi.ioriginate.services.dwr.AddressValidation.validate(AddressValidation.java:40) 在 sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 在 sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 在 sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 在 java.lang.reflect.Method.invoke(Method.java:483) 在 org.directwebremoting.impl.ExecuteAjaxFilter.doFilter(ExecuteAjaxFilter.java:34) 在 org.directwebremoting.impl.DefaultRemoter$1.doFilter(DefaultRemoter.java:428) 在 org.directwebremoting.impl.DefaultRemoter.execute(DefaultRemoter.java:431) 在 org.directwebremoting.impl.DefaultRemoter.execute(DefaultRemoter.java:283) 在 org.directwebremoting.servlet.PlainCallHandler.handle(PlainCallHandler.java:52) 在 org.directwebremoting.servlet.UrlProcessor.handle(UrlProcessor.java:101) 在 org.directwebremoting.servlet.DwrServlet.doPost(DwrServlet.java:146) 在 javax.servlet.http.HttpServlet.service(HttpServlet.java:754) 在 javax.servlet.http.HttpServlet.service(HttpServlet.java:847) 在 org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:324) 在 org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:242) 在 org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) 在 org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:118) 在 org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:84) 在 org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 在 org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:113) 在 org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 在 org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:103) 在 org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 在 org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:113) 在 org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 在 org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:154) 在 org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 在 org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:45) 在 org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 在 org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilter(BasicAuthenticationFilter.java:201) 在 org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 在 org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter.doFilter(DefaultLoginPageGeneratingFilter.java:155) 在 org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 在 org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:199) 在 org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 在 org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110) 在 org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 在 org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50) 在 org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) 在 org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 在 org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87) 在 org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 在 org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192) 在 org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160) 在 org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:344) 在 org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:261) 在 org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:274) 在 org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:242) 在 org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:275) 在 org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:161) 在 org.jboss.web.tomcat.security.SecurityAssociationValve.invoke(SecurityAssociationValve.java:181) 在 org.jboss.modcluster.catalina.CatalinaContext$RequestListenerValve.event(CatalinaContext.java:285) 在 org.jboss.modcluster.catalina.CatalinaContext$RequestListenerValve.invoke(CatalinaContext.java:261) 在 org.jboss.web.tomcat.security.JaccContextValve.invoke(JaccContextValve.java:88) 在 org.jboss.web.tomcat.security.SecurityContextEstablishmentValve.invoke(SecurityContextEstablishmentValve.java:100) 在 org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:159) 在 org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102) 在 org.jboss.web.tomcat.service.jca.CachedConnectionValve.invoke(CachedConnectionValve.java:158) 在 org.apache.catalina.valves.RequestDumperValve.invoke(RequestDumperValve.java:151) 在 org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109) 在 org.jboss.web.tomcat.service.request.ActiveRequestResponseCacheValve.invoke(ActiveRequestResponseCacheValve.java:53) 在 org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:362) 在 org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:877) 在 org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:654) 在 org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:951) 在 java.lang.Thread.run(Thread.java:745)

...在 EMS 方面,我看到一些 XA 错误:

johan@my_machine:~/opt/jboss-6.1.0.Final/bin$ 2014-10-02 10:43:15.801 错误:不存在的消费者的交易:15 connID=16 sessID=20 formatID=131076 gtrid_length=29 bqual_length=28 数据=%00%00%00%00%00%00%00%00%00%00%FF%FF%7F%00%01%01%00%00%12jT-%0D*%00%00%00 %AF1%00%00%00%00%00%00%00%00%00%00%FF%FF%7F%00%01%01%00%00%12jT-%0D*%00%00%00 %B1 2014-10-02 10:43:15.832 错误:处理 xa 结束时出错 - 事务 标记为 ROLLBACKONLY,异常。 connID=16 sessID=20 formatID=131076 gtrid_length=29 bqual_length=28 数据=%00%00%00%00%00%00%00%00%00%00%FF%FF%7F%00%01%01%00%00%12jT-%0D*%00%00%00 %AF1%00%00%00%00%00%00%00%00%00%00%FF%FF%7F%00%01%01%00%00%12jT-%0D*%00%00%00 %B1

查看堆栈跟踪后,我打开 Spring 的 AbstractPlatformTransactionManager 的源代码,发现以下处理 REQUIRES_NEW 的代码(从第 415 行开始):

if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) 
    if (debugEnabled) 
        logger.debug("Suspending current transaction, creating new transaction with name [" +
                definition.getName() + "]");
    
    SuspendedResourcesHolder suspendedResources = suspend(transaction);
    try 
        boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
        DefaultTransactionStatus status = newTransactionStatus(
                definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
        doBegin(transaction, definition);
        prepareSynchronization(status, definition);
        return status;
    
    catch (RuntimeException beginEx) 
        resumeAfterBeginException(transaction, suspendedResources, beginEx);
        throw beginEx;
    
    catch (Error beginErr) 
        resumeAfterBeginException(transaction, suspendedResources, beginErr);
        throw beginErr;
    

我的问题是:新事务在哪里开始?

从表面上看,似乎正在使用现有事务而不是正在启动新事务 - 看看“事务”是如何传递给 doBegin(...) 的。我也查看了 doBegin,没有迹象表明正在请求或创建新事务。我从 JBoss 获得的堆栈跟踪和警告似乎支持此视图...

【问题讨论】:

我不确定看那里是否会对您有所帮助。我在 JBoss 中多次看到此异常,这要么是 JBoss 本身的错误,要么是错误使用事务语义。如果您使用的是 4.1.x,那么在 JmsTemplate 上使用新的 sendAndReceive 怎么样? Going 需要 new 立即发送消息(如果我了解您的用例)是个坏主意 1.就像我说的:我正在使用 JBoss 6.1.0-Final 2。sendAndReceive 在这种情况下不起作用。当您尝试时,事件的顺序如下:交易开始 B.您发送消息 c.你等待回复(它永远不会来,因为......) d。交易超时 只有在事务提交的时候才会发送消息,所以如果你想做request-reply,你需要在一个单独的事务中发送请求,然后再去等待reply...跨度> 也许您需要使用非 XA ConnectionFactory 创建您的 JmsTemplatesendAndReceive 方法无论如何都要求非事务性会话。如果这对您不起作用,请提交一个重现该问题的项目的问题。 不,这对我不起作用。我必须使用 XA 连接工厂。 【参考方案1】:

很高兴看到我不是一个人被困在这个无光的洞里......

据我所知,此警告的深层原因描述为here(线程结束)

当外部事务被挂起并且新的内部事务 已启动,Jboss 连接池检索到的托管连接 内部事务与外部事务相同 事务,这导致 IllegalStateException 被抛出!

并且是由于 jboss JCA 合同实施的特定行为 (Lazy JCA enlistment)。

A defect has been opened on spring side,标记为“无法修复”,但它们提供了一种解决方法配置:

典型的解决方案是使用 Spring 的 TransactionAwareDataSourceProxy 并切换 那里的“reobtainTransactionalConnections”标志为“true”

玩得开心!

【讨论】:

谢谢!这很有意义

以上是关于Spring 是不是真的使用 REQUIRES_NEW 启动新事务?的主要内容,如果未能解决你的问题,请参考以下文章

“春运”的英文真的不是 Spring Transportation!(文末有春节福利)

什么鬼?弃用JDK动态代理,Spring5 默认使用 CGLIB 了?

Spring @transactional 是不是与 MongoDB 一起使用?

惊人!Spring5 AOP 默认使用 CGLIB ?从现象到源码的深度分析

GitHub标星75k,阿里15W字的Spring高级文档(全彩版),真的太香了

Spring 5 AOP 默认改用 CGLIB 了?从现象到源码的深度分析