In spring the HibernateTransactionManager uses the SessionFactory it was initialised with to "bind" a Session to the current thread context when creating a new transaction. Then when HibernateTemplate is used it find that bound Session and uses it.
However I found today that HTM also binds its transaction to the underlying DataSource as well as the SessionFactory (if possible). This allows code to use JdbcTemplate within the transaction scope and, provided the DataSource used by JdbcTemplate is the same as the SessionFactory uses, the Jdbc operations will participate in the transaction (using the same underlying Connection).
This bit me quite badly today when I had some code in my hibernate id allocator that was creating a DataSourceTransactionManager and JdbcTemplate to allocate ids out of a high-lo table. I was intending that this be a standalone transaction that would fetch the next high number and then commit the change to the id table. However because of the above behaviour it was actually participating in my "outer" hibernate transaction AND even worse committing it early. Suffice to say not good.
I tried playing around with transaction propogation settings (used REQUIRES_NEW) but this didn't help.
Does anyone know the best way to use JdbcTemplate within a hibernate transaction and NOT have them share a transaction, even tho they share the same DataSource?
EDIT:
I have a SessionFactory (S) which is created by the spring LocalSessionFactoryBean using a DataSource (D). The HibernateTransactionManager is created with that SessionFactory (S).
some business logic code would look like this..
hibernateTransactionOperations.execute( new TransactionCallbackWithoutResult()
{
@Override
protected void doInTransactionWithoutResult( TransactionStatus status )
{
// some transactional code here using a HibernateTemplate
// will include calls to id allocation when doing hibernateTemplate.save(obj)
}
} );
my id allocation does this (paraphrased), the DataSource below is the same (D) as the one used in the SessionFactory (S).
PlatformTransactionManager txManager = new DataSourceTransactionManager( dataSource );
TransactionOperations transactionOperations = new TransactionTemplate( txManager );
return transactionOperations.execute( new TransactionCallback<Long>()
{
public Long doInTransaction( TransactionStatus status )
{
return allocateBatchTxn( idKey, batchSize );
}
} );
When the transactionOperations execute above completes it will commit the underlying transaction which seems to be the same as the 'outer' hibernate transaction. I have confirmed this by checking locks/transactions in the DB.
DataSourceTransactionManager
should initiate a new tx and new connection independent of the Hibernate tx, so you're along the right lines. Can you show us some code/config to illustrate what you've done? - skaffman