Announcement Announcement Module
Collapse
No announcement yet.
Session not flushed with Hibernate 4.1.3, JTA and Spring transaction management integ Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Session not flushed with Hibernate 4.1.3, JTA and Spring transaction management integ

    I have been migrating from hibernate 3.3.x to hibernate 4.1.3-final using Spring 3.1.1-release.
    Besides the hibernate specific refactorings (due to API changes) I thought the only changes for the Spring integration were changing packages names:

    -Use the LocalSessionFactoryBean from the hibernate4 package
    -Use the HibernateTransactionManger (which we only use for testing) from the hibernate4 package.

    As it turned out the migration when smooth and everything was working on a local tomcat.
    However, once we ran our app on glassfish with the JtaTransactionManager (I tested it on GF3.1.2) we got a "No Session found for current thread" when obtaining sessionFactory.currentSession().
    After checking SpringSessionContext, we learned that if TransactionSynchronizationManager does not return a existing session or SessionHolder, a check
    is performed for a jtaSessionContext, which was null.
    The fact that no SessionHolder is registered made also sense as this done by the HibernateTransactionManager and we are using the JtaTransactionManager.

    So then learned that in case of JTA you have to specify manually how the tx manager/user transaction can be found.
    This was done automatically for you in the hibernate3 LocalSessionFactoryBean, but no longer in the hibernate4 LocalSessionFactoryBean.
    So to solve this we configured:

    hibernate.transaction.jta.platform and set it to SunOneJtaPlatform.

    This resolved the "No Session found for current thread" as it initialized the jtaSessionContext with the txmanager provided by the configured JtaPlatform.

    However, now it turns out that the hibernate session is not flushed before the transaction is commited and hence no modifications are written to database.
    In the supplied sample we have a basic hibernate setup with H2. Next we have a JtaTransactionManager and a transactional facade.
    Next we have a test entity having a single property. The facade has two methods, one to store the entity and one to retrieve the entity.
    They both marked as @Transactional and if called will run in there own transaction.
    The trigger is a plain JEE servlet which retrieves the facade from application context.
    First the store method is called (tx1) then the retrieve method is called (tx2).
    As you will see with the Spring hibernate4 integration there was nothing saved.
    With the hibernate3 integration everything works as expected and the record is saved
    (it can be retrieved by the subsequent retrieval transaction)

    What is also bizarre is that in hibernate3 modus everything goes via the Spring TransactionSynchronizationManager (even in JTA mode).
    Also the current session is bound via a thread local referenced via the synchronization.
    This is bound using a SpringSessionSynchronization which will call flush before transaction completion.

    All of this is gone with the hibernate4 integration from the moment a JTA environment is detected.
    As of then everything goes via the JTA transaction manager, as there where no Spring Transacion management involved.
    This could be normal to a certain extend, but it feels odd compared to the way is was done with hibernate3

    I supplied two samples:

    -hibernate3.zip : this is the working one, deploy it on GF and goto "http://localhost:8080/hibernate3/Persist"
    You will see that it stores a record and is able to retrieve it again

    -hibernate4.zip : the exact same sample as above, but now with hibernate4 and using LocalSessionFactoryBean from the hibernate4 package and the hibernate.transaction.jta.platform set.
    You will see that it stores a record and is NOT able to retrieve it.

    Both samples have a POM so it should be trivial to build them.

  • #2
    Im having almost the same problem.

    Ive set hibernate.transaction.factory_class to org.hibernate.engine.transaction.internal.jta.CMTT ransactionFactory and hibernate.transaction.jta.platform to org.hibernate.service.jta.platform.internal.JBossA ppServerJtaPlatform. My transaction manager bean is a org.springframework.transaction.jta.JtaTransaction Manager configured bean.

    Then, logging (TRACE mode) shows me the following info:

    15:23:58,555 TRACE [org.springframework.transaction.interceptor.Transa ctionInterceptor] (http--127.0.0.1-8080-1) Completing transaction for...

    15:23:58,557 TRACE [org.springframework.transaction.jta.JtaTransaction Manager] (http--127.0.0.1-8080-1) Triggering beforeCommit synchronization
    15:23:58,558 TRACE [org.springframework.transaction.jta.JtaTransaction Manager] (http--127.0.0.1-8080-1) Triggering beforeCompletion synchronization
    15:23:58,559 DEBUG [org.springframework.orm.jpa.EntityManagerFactoryUt ils] (http--127.0.0.1-8080-1) Closing JPA EntityManager
    15:23:58,560 DEBUG [org.springframework.transaction.jta.JtaTransaction Manager] (http--127.0.0.1-8080-1) Initiating transaction commit
    15:23:58,561 TRACE [org.hibernate.engine.transaction.synchronization.i nternal.RegisteredSynchronization] (http--127.0.0.1-8080-1) JTA sync : beforeCompletion()
    15:23:58,562 TRACE [org.hibernate.engine.transaction.synchronization.i nternal.SynchronizationCallbackCoordinatorImpl] (http--127.0.0.1-8080-1) Transaction before completion callback
    15:23:58,564 TRACE [org.hibernate.internal.SessionImpl] (http--127.0.0.1-8080-1) before transaction completion
    15:23:58,566 TRACE [org.hibernate.engine.transaction.synchronization.i nternal.RegisteredSynchronization] (http--127.0.0.1-8080-1) JTA sync : afterCompletion(3)
    15:23:58,567 TRACE [org.hibernate.engine.transaction.synchronization.i nternal.SynchronizationCallbackCoordinatorImpl] (http--127.0.0.1-8080-1) Transaction after completion callback [status=3]
    15:23:58,568 TRACE [org.hibernate.engine.transaction.internal.Transact ionCoordinatorImpl] (http--127.0.0.1-8080-1) after transaction completion
    15:23:58,569 DEBUG [org.hibernate.engine.jdbc.internal.LogicalConnecti onImpl] (http--127.0.0.1-8080-1) Aggressively releasing JDBC connection
    15:23:58,570 TRACE [org.hibernate.internal.SessionImpl] (http--127.0.0.1-8080-1) after transaction completion
    15:23:58,571 DEBUG [org.hibernate.ejb.EntityManagerImpl] (http--127.0.0.1-8080-1) Closing entity manager after transaction completion
    15:23:58,572 TRACE [org.hibernate.internal.SessionImpl] (http--127.0.0.1-8080-1) Closing session
    15:23:58,573 TRACE [org.hibernate.engine.jdbc.internal.LogicalConnecti onImpl] (http--127.0.0.1-8080-1) Closing logical connection
    15:23:58,574 TRACE [org.hibernate.engine.jdbc.internal.proxy.Connectio nProxyHandler] (http--127.0.0.1-8080-1) Handling invocation of connection method [close]
    15:23:58,575 TRACE [org.hibernate.engine.jdbc.internal.proxy.Connectio nProxyHandler] (http--127.0.0.1-8080-1) Invalidating connection handle
    15:23:58,576 TRACE [org.hibernate.engine.jdbc.internal.JdbcResourceReg istryImpl] (http--127.0.0.1-8080-1) Closing JDBC container [org.hibernate.engine.jdbc.internal.JdbcResourceReg istryImpl@17072bf]
    ...

    HOWEVER IF I use org.springframework.orm.jpa.JpaTransactionManager, instead of JtaTransactionManager, adjusting the configurations above, logging shows me:

    16:40:36,014 TRACE [org.springframework.transaction.interceptor.Transa ctionInterceptor] (http--127.0.0.1-8080-1) Completing transaction for...

    16:40:36,015 TRACE [org.springframework.orm.jpa.JpaTransactionManager] (http--127.0.0.1-8080-1) Triggering beforeCommit synchronization
    16:40:36,015 TRACE [org.springframework.orm.jpa.JpaTransactionManager] (http--127.0.0.1-8080-1) Triggering beforeCompletion synchronization
    16:40:36,016 DEBUG [org.springframework.orm.jpa.JpaTransactionManager] (http--127.0.0.1-8080-1) Initiating transaction commit
    16:40:36,016 DEBUG [org.springframework.orm.jpa.JpaTransactionManager] (http--127.0.0.1-8080-1) Committing JPA transaction on EntityManager [org.hibernate.ejb.EntityManagerImpl@2d885a]
    16:40:36,017 DEBUG [org.hibernate.engine.transaction.spi.AbstractTrans actionImpl] (http--127.0.0.1-8080-1) committing
    16:40:36,017 TRACE [org.hibernate.internal.SessionImpl] (http--127.0.0.1-8080-1) Automatically flushing session
    16:40:36,017 TRACE [org.hibernate.event.internal.AbstractFlushingEvent Listener] (http--127.0.0.1-8080-1) Flushing session
    16:40:36,018 DEBUG [org.hibernate.event.internal.AbstractFlushingEvent Listener] (http--127.0.0.1-8080-1) Processing flush-time cascades
    16:40:36,018 TRACE [org.hibernate.engine.internal.Cascade] (http--127.0.0.1-8080-1) Processing cascade ACTION_PERSIST_ON_FLUSH for: br.gov.tcu.sgos.modelo.entidade.ordemservico.Ordem Servico
    16:40:36,019 TRACE [org.hibernate.engine.spi.CascadingAction] (http--127.0.0.1-8080-1) Cascading to persist on flush: br.gov.tcu.sgos.modelo.entidade.contrato.Contrato
    16:40:36,020 TRACE [org.hibernate.engine.internal.Cascade] (http--127.0.0.1-8080-1) Done processing cascade ACTION_PERSIST_ON_FLUSH for: br.gov.tcu.sgos.modelo.entidade.ordemservico.Ordem Servico
    16:40:36,021 DEBUG [org.hibernate.event.internal.AbstractFlushingEvent Listener] (http--127.0.0.1-8080-1) Dirty checking collections
    16:40:36,021 TRACE [org.hibernate.event.internal.AbstractFlushingEvent Listener] (http--127.0.0.1-8080-1) Flushing entities and processing referenced collections
    16:40:36,022 TRACE [org.hibernate.persister.entity.AbstractEntityPersi ster] (http--127.0.0.1-8080-1) br.gov.tcu.sgos.modelo.entidade.ordemservico.Ordem Servico.descricao is dirty
    16:40:36,022 TRACE [org.hibernate.event.internal.DefaultFlushEntityEve ntListener] (http--127.0.0.1-8080-1) Found dirty properties [[br.gov.tcu.sgos.modelo.entidade.ordemservico.Ordem Servico#5]] : [Ljava.lang.String;@1882506
    16:40:36,023 TRACE [org.hibernate.event.internal.DefaultFlushEntityEve ntListener] (http--127.0.0.1-8080-1) Updating entity: [br.gov.tcu.sgos.modelo.entidade.ordemservico.Ordem Servico#5]
    16:40:36,024 TRACE [org.hibernate.engine.internal.Versioning] (http--127.0.0.1-8080-1) Incrementing: 2 to 3
    16:40:36,026 TRACE [org.hibernate.event.internal.AbstractFlushingEvent Listener] (http--127.0.0.1-8080-1) Processing unreferenced collections
    16:40:36,026 TRACE [org.hibernate.event.internal.AbstractFlushingEvent Listener] (http--127.0.0.1-8080-1) Scheduling collection removes/(re)creates/updates
    16:40:36,027 DEBUG [org.hibernate.event.internal.AbstractFlushingEvent Listener] (http--127.0.0.1-8080-1) Flushed: 0 insertions, 1 updates, 0 deletions to 1 objects

    SO, is something missing? Some additional configuration? Using JpaTransactionaManager everything works as expected, but when using JtaTransactionManager there is no auto flush during the before completion phase.

    Could you please help us?

    Im looking forward to hearing good news from the Spring guys.

    Thank you.

    Comment


    • #3
      alysson.rodrigues: The JpaTransactionManager will probably act the same way as the HibernateTransactionManager.
      If I replace the JtaTransactionManager with the HibernateTransactionManager everything also works as expected, and I also see the flush.
      However, the Jpa/HibernateTransactionManager are single resource transaction managers and cannot be used if multiple resources need to participate in the transaction.
      So switching to the HibernateTransactionManager is not an option.

      What happens in case of direct hibernate access is that the SpringSessionContext will detect a SessionHolder object registered by the HibernateTransactionManager.
      In that case it will register a SpringSessionSynchronization (SpringSessionContext L75): TransactionSynchronizationManager.registerSynchron ization(new SpringSessionSynchronization(sessionHolder, this.sessionFactory)); which will eventually flush the session before the transaction is commited.
      I suspect that in case of the JpaTransactionManager something similar is happening.

      However, if JTA mode is detected no such synchronization are registered and no one is flushing the session.

      The only workaround this far is creating an aspect that listens on the @Transactional annotation and flushes the session.
      You can change the order of the <tx:annotation-driven/> (or @EnableTransactionManagement annotation) using the "order" attribute.
      You would then give the <tx:annotation-driven/> a lower order then your aspect listing for the @Transactional annotation, this way your aspect is called after the transaction is started and before it is committed (allowing it to flush the session).

      Btw; I created an issue for this: https://jira.springsource.org/browse/SPR-9404 feel free to vote for it

      Comment


      • #4
        koen.serneels: what about the SpringSessionContext.currentSession L89 and the SpringFlushSynchronization class itself?
        <code>
        else if (this.jtaSessionContext != null) {
        Session session = this.jtaSessionContext.currentSession();
        if (TransactionSynchronizationManager.isSynchronizati onActive()) {
        TransactionSynchronizationManager.registerSynchron ization(new SpringFlushSynchronization(session));
        }
        return session;
        }
        </code>

        Are not these lines and class supposed to do the same work as you mentioned?

        Logging shows me that Spring has detected the transaction manager, user transaction and a transaction synchronization registry. I was wondering if I have to set something to get it working with SpringFlushSynchronization.

        17:38:58,948 DEBUG [org.springframework.jndi.JndiTemplate] (MSC service thread 1-7) Looking up JNDI object with name [java:/TransactionManager]
        17:38:58,949 DEBUG [org.springframework.transaction.jta.JtaTransaction Manager] (MSC service thread 1-7) JTA TransactionManager found at fallback JNDI location [java:/TransactionManager]
        17:38:58,950 INFO [org.springframework.transaction.jta.JtaTransaction Manager] (MSC service thread 1-7) Using JTA UserTransaction: org.jboss.tm.usertx.client.ServerVMClientUserTrans action@192a63a
        17:38:58,950 INFO [org.springframework.transaction.jta.JtaTransaction Manager] (MSC service thread 1-7) Using JTA TransactionManager: com.arjuna.ats.jbossatx.jta.TransactionManagerDele gate@4383e3
        17:38:58,951 DEBUG [org.springframework.jndi.JndiTemplate] (MSC service thread 1-7) Looking up JNDI object with name [java:comp/TransactionSynchronizationRegistry]
        17:38:58,952 DEBUG [org.springframework.transaction.jta.JtaTransaction Manager] (MSC service thread 1-7) JTA TransactionSynchronizationRegistry found at default JNDI location [java:comp/TransactionSynchronizationRegistry]
        17:38:58,953 INFO [org.springframework.transaction.jta.JtaTransaction Manager] (MSC service thread 1-7) Using JTA TransactionSynchronizationRegistry: com.arjuna.ats.internal.jta.transaction.arjunacore .TransactionSynchronizationReg
        istryImple@87e18f

        Thank you for answering me in such a good manner!

        Comment


        • #5
          alysson.rodrigues: Yes, the code you mention is indeed the code which gets executed in case of a JTA context.
          You would indeed also think that the "SpringFlushSynchronization" (as the name implies) will flush the session.
          But that is not happening. If you look for references you will notice that TransactionStatus.flush() is only called from classes in the JDO package. So the synchronization registered here is not invoked. Why, I don't know...

          So the main question remains, is this a bug or is there (like you said) some missing configuration...

          Comment


          • #6
            Apparently it could be solved by setting the property hibernate.transaction.factory_class which resolves default to the JDBC transaction factory depending on the hibernate transaction API. See: https://jira.springsource.org/browse/SPR-9404

            Comment


            • #7
              Intrigued by the problem I did some comparisons between the hibernate3 and hibernate4.

              In the hibernate 3 code when there is no jtaTransactionManager set it registers the SpringTransactionFactory which, according to the javadoc, is also aware of Spring driven JTA transactions. This class (which is a hibernate3 class) isn't being set (nor available) for hibernate4. Which then defaults to the JDBCTransactionFactory from hibernate which obviously isn't aware of the spring JTA transaction.

              That seems to be a missing piece which is solved by explicitly specifying the hibernate.transaction.factory_class.
              Last edited by Marten Deinum; May 29th, 2012, 09:00 AM.

              Comment


              • #8
                There seems yet again another issue when using the Spring 3.1 - Hibernate4 integration.
                This issue pops up so late since it only occurs in special circumstances: it has to do with the hibernate "smart" flushing which is not working.

                Hibernate guarantees that every modification you make (CUD) is flushed to the database prior any other operation that can possibly be related to the outstanding queries.
                This is to ensure that you don't work with stale data within the same transaction.

                For example:

                1. Save an entity
                2. Update the property of an entity
                3. Query the entity using the value of the property changed in the previous step in the where clause

                Hibernate will make sure that the changes by step2 are flushed before step3 is executed (smart flush).
                If this didn't happen we would never be able to retrieve the entity in step3.
                The problem is that this smart flushing is not happening any more, since Hibernate does not detect that it is in a transaction.

                Taken from SessionImpl L1178:

                Code:
                protected boolean autoFlushIfRequired(Set querySpaces) throws HibernateException {
                		errorIfClosed();
                		if ( ! isTransactionInProgress() ) {
                			// do not auto-flush while outside a transaction
                			return false;
                		}
                		AutoFlushEvent event = new AutoFlushEvent( querySpaces, this );
                		for ( AutoFlushEventListener listener : listeners( EventType.AUTO_FLUSH ) ) {
                			listener.onAutoFlush( event );
                		}
                		return event.isFlushRequired();
                	}
                What happens is before step3 is excecuted 'autoFlushIfRequired' is called (good).
                However isTransactionInProgress() will returns false.
                If you drill down in the code, you will see that it will call : transactionCoordinator.isTransactionInProgress() which will then call getTransaction().isActive(), which delegates to JtaTransaction L237:

                Code:
                	@Override
                	public boolean isActive() throws HibernateException {
                		if ( getLocalStatus() != LocalStatus.ACTIVE ) {
                			return false;
                		}
                
                		final int status;
                		try {
                			status = userTransaction.getStatus();
                		}
                		catch ( SystemException se ) {
                			throw new TransactionException( "Could not determine transaction status: ", se );
                		}
                		return JtaStatusHelper.isActive( status );
                	}
                The LocalStatus will be "NOT_ACTIVE" and the userTransaction is null. Why? Because no one called "begin" on the JtaTransaction.
                In case of the HibernateTransactionManager it will call begin() once a transaction is started (in that case it will be JdbcTransaction rather then JtaTransaction).
                So while there is a transaction started and everything is working nicely there is still a part of Hibernate which is unaware that a transaction is indeed active, which results in strange behaviour like illustrated here.

                Afaik this is a bug in the integration, since there are no more secret properties we can use to fix this one.

                Added jira: https://jira.springsource.org/browse/SPR-9480
                Last edited by koen.serneels; Jun 7th, 2012, 04:55 AM.

                Comment

                Working...
                X