【问题标题】:Can't join JDBC transaction with JTA transaction with Hibernate 4/Hibernate 5 on Glassfish 4 when flush() is manually called手动调用 flush() 时,无法在 Glassfish 4 上使用 Hibernate 4/Hibernate 5 将 JDBC 事务与 JTA 事务结合起来
【发布时间】:2017-05-07 01:08:04
【问题描述】:

我正在尝试推进我们的应用程序。现在它在 Glassfish 3、JAVA EE 6 下运行,并使用 Hibernate 3 作为 JPA 实现。 我写了一个例子,显示了我在交易中遇到的问题。在某些情况下,应用程序需要手动调用实体管理器的 flush() 方法。但即使在 JTA 环境中,flush() 也会导致对底层数据库的物理提交(我们使用 ojdbc6.jar JDBC 驱动程序连接到 ORACLE X/XI)。这不是预期的行为,因为 JDBC 事务应该加入 JTA 事务。如果在 flush() 之后 EJB 引发异常,则应回滚刷新的数据。 在 Hibernate 3 中,一切正常。在 Hibernate 4.3.5 中它没有。

调试 Hibernate 4.3 代码我发现了这个

package org.hibernate.jpa.internal.EntityManagerImpl

           @Override
           protected Session internalGetSession() {
                          if ( session == null ) {
                                        ...
                                        SessionBuilderImplementor sessionBuilder = internalGetEntityManagerFactory().getSessionFactory().withOptions();

--->>>>>>          sessionBuilder.autoJoinTransactions( getTransactionType() != PersistenceUnitTransactionType.JTA ); <<<<<<-----

                                        session = sessionBuilder.openSession();
                          }
                          return session;
           }

这会导致 org.hibernate.engine.transaction.internal.TransactionCoordinatorImpl.attemptToRegisterJtaSync() 方法进入此块并跳过与 JTA 平台的同步..

 ....
           final JoinStatus joinStatus = currentHibernateTransaction.getJoinStatus();
           if ( joinStatus != JoinStatus.JOINED ) {
                                        // the transaction is not (yet) joined, see if we should join...
 ---->>>>>            if ( !transactionContext.shouldAutoJoinTransaction() ) {  <<<<<<----
                                                       // we are supposed to not auto join transactions; if the transaction is not marked for join
                                                       // we cannot go any further in attempting to join (register sync).
                                                       if ( joinStatus != JoinStatus.MARKED_FOR_JOINED ) {
                                                                      if (isDebugging) {
                                                                                     LOG.debug( "Skipping JTA sync registration due to auto join checking" );
                                                                      }
                                                                      return;
                                                       }
                                        }
                          }

我尝试调试 Hibernate 5,这里 AutoJoinTransaction 问题消失了,大部分事务代码已被重写。无论如何,flush() 问题仍然存在。

我写了一个小例子来演示它。

@Stateless
public class BaseServicesAdapterImpl implements BaseServicesAdapterInterface {

    @PersistenceContext 
    protected EntityManager em;

    @Resource SessionContext sessionContext;

    public EntityManager getEm() {
        return em;
    }

    public void setEm(EntityManager em) {
        this.em = em;
    }

    @Override
    public String test() {
        persistEntityAndFlush();
        return "finished";
    }


    private void persistEntityAndFlush() {
        persistEntityAndFlush("TST", new Integer(5), new Long(-1), new Long(-1), 
                new Date(), "", null, new Date(), new Integer(10), "test transaction", 
                null, null, null, "");      
    }


    private Long persistEntityAndFlush(String tipoProcesso, Integer tpEntita, Long idEntita, Long progr,
            Date date, String stato, String subStato, Date dtStato, Integer ggTimer,
            String descrizione, String idPrcPerif1, String idPrcPerif2, String idPrcPerif3, String chiave1) {
            Long idProcesso = 0L;

            TestEntity testEntity = new TestEntity();
            testEntity.setTpEntita(tpEntita);
            testEntity.setIdEntita(idEntita);
            testEntity.setIdProcesso(idProcesso);

            //.....

            getEm().persist(testEntity);

            //***** THIS FLUSH COMMITS THE DATA *****
            getEm().flush(); 

            throw new RuntimeException("Too late?");
    }


}

这是 persistence.xml 文件

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
    version="1.0">

    <persistence-unit name="test_pu" transaction-type="JTA">
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <jta-data-source>dsName</jta-data-source>
        <properties>
            <property name="hibernate.hbm2ddl.auto" value="none" />
            <property name="hibernate.show_sql" value="true" />
            <property name="hibernate.format_sql" value="false" />
            <property name="hibernate.use_sql_comments" value="false" />
            <property name="hibernate.generate_statistics" value="false" />
            <property name="hibernate.ejb.metamodel.generation" value="disabled" />

            <!-- removing this does not affect anything -->
            <property name="hibernate.connection.release_mode" value="after_transaction" />

            <property name="hibernate.dialect" value="org.hibernate.dialect.Oracle10gDialect" />
            <property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.SunOneJtaPlatform" />            
        </properties>
    </persistence-unit>
</persistence>

我错过了什么? 任何帮助将不胜感激。 提前致谢,

大卫·奥伯

【问题讨论】:

  • GlassFish 通常不使用 Hibernate,而是使用 EclipseLink。您是如何将 Hibernate 集成到 GlassFish 中的?这种集成可能是原因吗?
  • 我们刚刚在 EAR 中添加了它。集成可能是问题,但不应该是。在 GF3 中不是这样。 Hibernate 有一个为 Glassfish 提供特定实现的策略选择器。 Wildfly 9 上部署的同一个 EAR 正确地回滚了事务。所以问题可能出在 Hibernate“面向 Glassfish”的类或 Glassfish 本身中。迁移到另一个 JPA 实现(例如 EclipseLink)不是我们的选择。

标签: hibernate jpa transactions jta flush


【解决方案1】:

我的错!我错过了资源引用元素! 我不清楚为什么这会导致问题,但添加它解决了它。

  <servers>
    <server config-ref="server-config" name="server">
  <!-- WITH THE FOLLOWING LINE THE TRANSACTION PROBLEM DISAPPEARS -->
      <resource-ref ref="MyDataSource"></resource-ref>
    </server>
  </servers>``

【讨论】:

    【解决方案2】:

    好吧,我看到你使用 EJB,EJB 中事务的默认模式是容器管理事务(CMT),在这种模式下,管理事务的责任来自 EJB 容器。它根据 CMT 模式下每个 Session Beans 方法的事务属性打开、关闭、中止。

    @TransactionManagement ( TransactionManagementType . CONTAINER )
    public class YourClass
    

    查看 Bean 托管事务 - BMT

    @TransactionManagement ( TransactionManagementType . BEAN )
    public class YouClass
    

    使用 BMT,您可以打开、关闭、中止等,责任由您的应用程序负责

    【讨论】:

    • 这不是重点。容器必须确保在事务回滚时管理的事务不提交数据。
    猜你喜欢
    • 1970-01-01
    • 2018-11-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-07
    • 2015-01-15
    • 2013-08-20
    • 1970-01-01
    相关资源
    最近更新 更多