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
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.
See the Bean Managed Transactions - BMT
Using BMT, you can open, close, abort etc, and the responsibility is from your application