Spring 休眠 "You cannot commit during a managed transaction!"

Spring Hibernate "You cannot commit during a managed transaction!"

将我的应用程序从 JSF 1.2 升级到 JSF 2.1 后,我在尝试登录时遇到以下 Spring Hibernate 异常:

DEBUG,[interceptor.ExceptionInterceptor][],org.springframework.transaction.TransactionSystemException: Could not commit Hibernate transaction; nested exception is org.hibernate.TransactionException: commit failed
    at org.springframework.orm.hibernate4.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:472)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:754)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:723)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:392)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:120)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at myapp.aop.interceptor.MethodExceptionInterceptor.invoke(MethodExceptionInterceptor.java:21)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at myapp.aop.interceptor.SessionActivityInterceptor.invoke(SessionActivityInterceptor.java:47)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:90)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)
    at com.sun.proxy.$Proxy121.endSession(Unknown Source)
    at myapp.web.action.connection.ConnectionBean.connect(ConnectionBean.java:100)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.jboss.seam.util.Reflections.invoke(Reflections.java:22)
    at org.jboss.seam.intercept.RootInvocationContext.proceed(RootInvocationContext.java:32)
    at org.jboss.seam.intercept.SeamInvocationContext.proceed(SeamInvocationContext.java:56)
    at myapp.web.interceptor.ExceptionInterceptor.aroundInvoke(ExceptionInterceptor.java:42)
    at org.jboss.seam.intercept.SeamInvocationContext.proceed(SeamInvocationContext.java:68)
    at org.jboss.seam.transaction.RollbackInterceptor.aroundInvoke(RollbackInterceptor.java:28)
    at org.jboss.seam.intercept.SeamInvocationContext.proceed(SeamInvocationContext.java:68)
    at org.jboss.seam.intercept.SeamInvocationContext.proceed(SeamInvocationContext.java:68)
    at org.jboss.seam.core.MethodContextInterceptor.aroundInvoke(MethodContextInterceptor.java:44)
    at org.jboss.seam.intercept.SeamInvocationContext.proceed(SeamInvocationContext.java:68)
    at org.jboss.seam.core.SynchronizationInterceptor.aroundInvoke(SynchronizationInterceptor.java:35)
    at org.jboss.seam.intercept.SeamInvocationContext.proceed(SeamInvocationContext.java:68)
    at org.jboss.seam.intercept.RootInterceptor.invoke(RootInterceptor.java:107)
    at org.jboss.seam.intercept.JavaBeanInterceptor.interceptInvocation(JavaBeanInterceptor.java:186)
    at org.jboss.seam.intercept.JavaBeanInterceptor.invoke(JavaBeanInterceptor.java:104)
    at myapp.web.action.connection.ConnectionBean_$$_javassist_seam_2.connect(ConnectionBean_$$_javassist_seam_2.java)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.jboss.el.util.ReflectionUtil.invokeMethod(ReflectionUtil.java:335)
    at org.jboss.el.util.ReflectionUtil.invokeMethod(ReflectionUtil.java:348)
    at org.jboss.el.parser.AstPropertySuffix.invoke(AstPropertySuffix.java:58)
    at org.jboss.el.parser.AstValue.invoke(AstValue.java:96)
    at org.jboss.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:276)
    at com.sun.faces.facelets.el.TagMethodExpression.invoke(TagMethodExpression.java:105)
    at org.apache.myfaces.trinidad.component.MethodExpressionMethodBinding.invoke(MethodExpressionMethodBinding.java:46)
    at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:101)
    at org.apache.myfaces.trinidad.component.UIXCommand.broadcast(UIXCommand.java:190)
    at javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:786)
    at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:1251)
    at oracle.adfinternal.view.faces.lifecycle.LifecycleImpl._invokeApplication(LifecycleImpl.java:1074)
    at oracle.adfinternal.view.faces.lifecycle.LifecycleImpl._executePhase(LifecycleImpl.java:402)
    at oracle.adfinternal.view.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:225)
    at javax.faces.webapp.FacesServlet.service(FacesServlet.java:593)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:295)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:214)
    at oracle.adfinternal.view.faces.webapp.rich.RegistrationFilter.doFilter(RegistrationFilter.java:105)
    at org.apache.myfaces.trinidadinternal.webapp.TrinidadFilterImpl$FilterListChain.doFilter(TrinidadFilterImpl.java:502)
    at oracle.adfinternal.view.faces.activedata.AdsFilter.doFilter(AdsFilter.java:60)
    at org.apache.myfaces.trinidadinternal.webapp.TrinidadFilterImpl$FilterListChain.doFilter(TrinidadFilterImpl.java:502)
    at org.apache.myfaces.trinidadinternal.webapp.TrinidadFilterImpl._doFilterImpl(TrinidadFilterImpl.java:327)
    at org.apache.myfaces.trinidadinternal.webapp.TrinidadFilterImpl.doFilter(TrinidadFilterImpl.java:229)
    at org.apache.myfaces.trinidad.webapp.TrinidadFilter.doFilter(TrinidadFilter.java:92)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:246)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:214)
    at org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:83)
    at org.jboss.seam.web.LoggingFilter.doFilter(LoggingFilter.java:60)
    at org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69)
    at myapp.web.filter.SessionActivityFilter.doFilter(SessionActivityFilter.java:67)
    at org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69)
    at org.jboss.seam.web.IdentityFilter.doFilter(IdentityFilter.java:40)
    at org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69)
    at org.jboss.seam.web.MultipartFilter.doFilter(MultipartFilter.java:90)
    at org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69)
    at org.jboss.seam.web.ExceptionFilter.doFilter(ExceptionFilter.java:64)
    at org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69)
    at org.jboss.seam.web.RedirectFilter.doFilter(RedirectFilter.java:45)
    at org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69)
    at org.jboss.seam.servlet.SeamFilter.doFilter(SeamFilter.java:158)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:246)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:214)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:230)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:149)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:389)
    at org.jboss.as.web.security.SecurityContextAssociationValve.invoke(SecurityContextAssociationValve.java:169)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:145)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:97)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:102)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:336)
    at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:856)
    at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:653)
    at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:920)
    at java.lang.Thread.run(Thread.java:724)
 Caused by: org.hibernate.TransactionException: commit failed
    at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.commit(AbstractTransactionImpl.java:185)
    at org.springframework.orm.hibernate4.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:468)
    ... 93 more
 Caused by: org.hibernate.TransactionException: unable to commit against JDBC connection
    at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.doCommit(JdbcTransaction.java:116)
    at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.commit(AbstractTransactionImpl.java:178)
    ... 94 more
 Caused by: java.sql.SQLException: You cannot commit during a managed transaction!
    at org.jboss.jca.adapters.jdbc.BaseWrapperManagedConnection.jdbcCommit(BaseWrapperManagedConnection.java:1052)
    at org.jboss.jca.adapters.jdbc.WrappedConnection.commit(WrappedConnection.java:757)
    at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.doCommit(JdbcTransaction.java:112)
    ... 95 more

这是我的 spring beans 配置:

    <bean id="parentSessionFactory" abstract="true">
        <property name="dataSource">
            <ref bean="myDS" />
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.connection.driver_class">
                    org.h2.Driver
                </prop>
                <prop key="hibernate.show_sql">false</prop>
                <prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</prop>
                <prop key="hibernate.cache.use_query_cache">true</prop>
                <prop key="hibernate.cache.use_second_level_cache">true</prop>
                <prop key="hibernate.cache.use_structured_cache">true</prop>                            
            </props>
        </property>
    </bean>

    <bean id="myDS"
          class="org.springframework.jndi.JndiObjectFactoryBean">
        <property name="jndiName" value="java:/myDS" />
    </bean>


    <bean id="sessionFactory" parent="parentSessionFactory"
      class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">   
      <property name="configLocation">
        <value>
            classpath:model/hibernate.cfg.xml
        </value>
      </property>

      <property name="hibernateProperties">
        <props merge="true">
            <prop key="hibernate.dialect">
                org.hibernate.dialect.H2Dialect
            </prop>    
        </props>        
      </property>
    </bean>

    <bean id="transactionManager"
      class="org.springframework.orm.hibernate4.HibernateTransactionManager">
      <property name="sessionFactory">
        <ref bean="sessionFactory"/>
      </property>
    </bean>


    <!-- the transactional advice (i.e. what 'happens'; see the <aop:advisor/> bean below) -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
    <!-- the transactional semantics... -->
      <tx:attributes>
        <!-- all methods starting with 'get' are read-only -->
        <tx:method name="get*" read-only="true" />
        <tx:method name="find*" read-only="true" />
        <tx:method name="list*" read-only="true" />
        <tx:method name="read*" read-only="true" />
        <tx:method name="save*" read-only="false" isolation="READ_COMMITTED"/>
        <tx:method name="remove*" read-only="false" isolation="READ_COMMITTED"/>
        <tx:method name="refresh*" read-only="false" isolation="READ_COMMITTED"/>
        <tx:method name="delete*" read-only="false" isolation="READ_COMMITTED"/>
        <!-- other methods use the default transaction settings (see below) -->
        <tx:method name="*" />
      </tx:attributes>
    </tx:advice>
    <tx:annotation-driven transaction-manager="transactionManager"/>

<!-- ensure that the above transactional advice runs for any execution
    of an operation defined by the *Service interface -->
    <aop:config>
        <aop:pointcut id="serviceOperation"
                      expression="execution(* services..*Service*.*(..))" />
        <aop:pointcut id="daoOperation"
                      expression="execution(* dao..*Dao.*(..))" />


        <aop:advisor advice-ref="sessionActivityInterceptor"
                     pointcut-ref="serviceOperation" />         
        <aop:advisor advice-ref="exceptionInterceptor"
                     pointcut-ref="serviceOperation" />      -->
        <aop:advisor advice-ref="txAdvice"
                     pointcut-ref="serviceOperation" />
        <aop:advisor advice-ref="txAdvice"
                     pointcut-ref="daoOperation" />      
    </aop:config>

这里是 hibernate.cfg.xml:

<hibernate-configuration>
    <session-factory>

        <property name="hibernate.dialect">org.hibernate.dialect.H2Dialect</property>
<!--        <property name="hibernate.cache.provider_class">net.sf.ehcache.hibernate.EhCacheProvider</property> -->
        <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
        <property name="hibernate.cache.use_query_cache">true</property>
        <property name="hibernate.cache.use_second_level_cache">true</property>


        <!-- Several mapping classes here -->
    </session-factory>
</hibernate-configuration>

@Transactional 注释仅在我的应用程序和单元测试中的一个方法中使用,但未用于应用程序的登录部分。

进一步调查我的异常,我看到在 BaseWrapperManagedConnection 中从 ironjacamar-jdbc-1.0.17 抛出异常的代码:

/**
    * JDBC commit
    * @exception SQLException Thrown if an error occurs
    */
   void jdbcCommit() throws SQLException
   {
      synchronized (stateLock)
      {
         if (inManagedTransaction)
            throw new SQLException("You cannot commit during a managed transaction!");

         if (jdbcAutoCommit)
            throw new SQLException("You cannot commit with autocommit set!");
      }
      con.commit();

      if (mcf.isJTA().booleanValue())
      {
         if (inLocalTransaction.getAndSet(false))
         {
            Collection<ConnectionEventListener> copy = null;
            synchronized (cels)
            {
               copy = new ArrayList<ConnectionEventListener>(cels);
            }

            ConnectionEvent ce = new ConnectionEvent(this, ConnectionEvent.LOCAL_TRANSACTION_COMMITTED);

            for (Iterator<ConnectionEventListener> i = copy.iterator(); i.hasNext();)
            {
               ConnectionEventListener cel = i.next();
               try
               {
                  cel.localTransactionCommitted(ce);
               }
               catch (Throwable t)
               {
                  if (trace)
                     getLog().trace("Error notifying of connection committed for listener: " + cel, t);
               }
            }
         }
      }
   }

变量 inManagedTransactionLocalManagedConnection begin 方法中设置为 true from ironjacamar-jdbc-1.0.17 :

/**
    * {@inheritDoc}
    */
   public void begin() throws ResourceException
   {
      lock();
      try
      {
         synchronized (stateLock)
         {
            if (!inManagedTransaction)
            {
               try
               {
                  if (underlyingAutoCommit)
                  {
                     underlyingAutoCommit = false;
                     con.setAutoCommit(false);
                  }
                  checkState();
                  inManagedTransaction = true;
               }
               catch (SQLException e)
               {
                  checkException(e);
               }
            }
            else
               throw new ResourceException("Trying to begin a nested local tx");
         }
      }
      finally
      {
         unlock();
      }
   }

唯一将此 inManagedTransaction 设置为 false 的地方是在 LocalManagedConnection commit()rollback() 方法中:

/**
    * {@inheritDoc}
    */
   public void commit() throws ResourceException
   {
      lock();
      try
      {
         synchronized (stateLock)
         {
            if (inManagedTransaction)
               inManagedTransaction = false;
         }
         try
         {
            con.commit();
         }
         catch (SQLException e)
         {
            checkException(e);
         }
      }
      finally
      {
         unlock();
      }
   }

   /**
    * {@inheritDoc}
    */
   public void rollback() throws ResourceException
   {
      lock();
      try
      {
         synchronized (stateLock)
         {
            if (inManagedTransaction)
               inManagedTransaction = false;
         }
         try
         {
            con.rollback();
         }
         catch (SQLException e)
         {
            try
            {
               checkException(e);
            }
            catch (Exception e2)
            {
               // Ignore
            }
         }
      }
      finally
      {
         unlock();
      }
   }

我有两个 ManagedConnection 一个是 BaseWrapperManagedConnection 另一个是 LocalManagedConnection 并且我正在经历他们的两种提交方法是否正常?

我还读到 JBoss 也可以处理事务(容器管理的事务)。是否有可能 Spring 和 JBoss 都试图处理我的会话和事务?

我正在使用 Hibernate 4.2 和 Spring 3.2.0。 其中大部分是经典配置,就像在网络上的许多教程中找到的一样,没什么特别的。使用此配置的应用程序使用 JSF 1.2 运行。 感谢您的任何建议,提示。

您可能在 JBoss 中配置了 JTA 数据源,这是一个托管事务资源,然后您使用的是非 JTA HibernateTransactionManager

要修复它,您有两种选择:

  1. 您可以使用 RESOURCE_LOCAL 数据源并通过 JNDI 提供它
  2. 您保留 JTA 数据源并将 Hibernate 配置为使用 Spring JtaTransactionManager with the actual Arjuna Transaction Manager

如果您的数据源使用 jta,则不能调用 transaction.begin 也不能提交那些由容器调用的事务划分。 如果您的数据源使用 RESOURCE_LOCAL,您必须划分所有事务,即使是只读事务。