简体   繁体   中英

Can't join JDBC transaction with JTA transaction with Hibernate 4/Hibernate 5 on Glassfish 4 when flush() is manually called

I'm trying to move our application forward. Now it runs under Glassfish 3, JAVA EE 6 and uses Hibernate 3 as the JPA implementation. I wrote an example that shows a problem I've with transactions. In some circumnstances the application needs to manually call the entity manager flush() method. But even in a JTA environment, what happens is that the flush() causes a physical commit on the underlying database (we're using ojdbc6.jar JDBC driver to connect to ORACLE X/XI). This is not the expected behaviour, due to the fact that the JDBC transaction should join the JTA transaction. If after the flush() an exception is raised by the EJB, the data flushed should be rollbacked. In Hibernate 3 everything works perfectly. In Hibernate 4.3.5 it doesn't.

Debugging the Hibernate 4.3 code I found this

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;
           }

This leads the org.hibernate.engine.transaction.internal.TransactionCoordinatorImpl.attemptToRegisterJtaSync() method to entering in this block and skipping the synchronization with the JTA platform..

 ....
           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;
                                                       }
                                        }
                          }

I tried to debug Hibernate 5 and here the AutoJoinTransaction problem disappeared, most of the transaction code has been rewritten. In any case, the flush() problem is still present.

I wrote a small example that demonstrates it.

@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?");
    }


}

This is the perstistence.xml file

<?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>

What am I missing? Any help will be appreciated. Thanks in advance,

David Obber

My mistake! I missed the resource-ref element! It's not clear to me why this causes the problem, but adding it solved it.

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

Well, I see that you use EJB, the default mode of transactions in EJB is Container Managed Transactions (CMT), in this mode, the responsibility of managing the transactions is from the EJB container. It opens, closes, aborts according to the transactional attribute of each Session Beans method in CMT mode.

@TransactionManagement ( TransactionManagementType . CONTAINER )
public class YourClass

See the Bean Managed Transactions - BMT

@TransactionManagement ( TransactionManagementType . BEAN )
public class YouClass

Using BMT, you can open, close, abort etc, and the responsibility is from your application

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM