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