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.
Don't create a new DataSourceTransactionManager in your id allocation code. Instead use REQUIRES_NEW and the HibernateTransactionManager.
In allocateBatchTxn(), the safest way to get the JDBC connection is via Spring's DataSourceUtils.getConnection() method.
I tried it with REQUIRES_NEW - it works as expected (on HSQLDB), perhaps it's DB-dependent:
// txManager is a HibernateTransactionManager obtained from the application context
TransactionOperations transactionOperations = new TransactionTemplate( txManager );
transactionOperations.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRES_NEW);
return transactionOperations.execute(new TransactionCallback<Long>() {
public Long doInTransaction( TransactionStatus status ) {
return allocateBatchTxn( idKey, batchSize );
}
});
Answering my own question.
The root cause of my problem is a couple of things in HibernateTransactionManager.
The setting 'autodetectDataSource' which defaults to true
In afterPropertiesSet() with the above true it auto-detects the DataSource from the SessionFactory
In doBegin() if the DataSource is not null it will bind new transactions to the SessionFactory AND the DataSource
This is causing my problem because also I have a new DataSourceTransactionManager it still uses the same underlying storage (TransactionSynchronizationManager) to manage transactions and because both use the DataSource you get this leaking of transactions between txn managers. I might argue that a txn manager should include its own 'key/id' in the key for the transactional resources so there are independent but it doesn't appear to do that.
The response above are sensible. Using the hibernate txn manager rather than creating a new DataSourceTransactionManager and then using REQURES_NEW would solve the problem. However in my case that would introduce a circular dependency between HTM -> SessionFactory -> IdAllocator -> HTM.
I came up a solution that works but isn't the most elegant thing ever.
When constructor the id allocator it is passed a DataSource in the constructor. I simply wrap that DataSource in a delegating wrapper that is 100% pass through. This changes the DataSource reference so the txn logic does not think there is a transaction in progress and works as I want it to.
Related
HI We are migrating hibernate 3 to 5.4.25 >Please help me to get CMT transactionfactory replacement in hibernate 5 as it is deprecated and i am getting no transaction is in progress if i use session.flush() in my DAO class GET methos. Kindly help
An excerpt from link
Its not just org.hibernate.transaction.CMTTransactionFactory that was
removed; all of the TransactionFactory classes were removed. Actually I
think we should add them as resolvable names:
org.hibernate.transaction.CMTTransactionFactory
->
org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorBuilderImpl
org.hibernate.transaction.JTATransactionFactory
-> org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorBuilderImpl
org.hibernate.transaction.JDBCTransactionFactory ->
org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorBuilderImpl
Actually, I have recently done a similar migration where I have completely changed the transaction-related blocks to Spring's imperative transaction infrastructure.
I have used PlatformTransactionManager which is the default implementations of this strategy interface are org.springframework.transaction.jta.JtaTransactionManager and org.springframework.jdbc.datasource.DataSourceTransactionManager, which can serve as an implementation guide for other transaction strategies.
#EnableTransactionManagement
#Configuration
public class DbConfig{
#Autowired
private DataSource dataSource;
#Bean
#Primary
public PlatformTransactionManager annotationDrivenTransactionManager(LocalSessionFactoryBean lsfb) {
return new HibernateTransactionManager(lsfb.getObject());
}
#Bean
#Primary
public LocalSessionFactoryBean getSessionFactory()
{
LocalSessionFactoryBean lsfb = new LocalSessionFactoryBean();
lsfb.setDataSource(this.dataSource);
Properties properties = new Properties();
properties.put("hibernate.dialect", <hibernateDialect>);
properties.put("hibernate.connection.isolation",2); // READ_COMMITTED
properties.put("hibernate.show_sql", hibernateShowSql); //To show sql or not
//...configurable hibernate properties to add
lsfb.setHibernateProperties(properties);
lsfb.setPackagesToScan("com.demo.entity.model"");
return lsfb;
}
}
JavaDoc about the PlatformTransactionManager(link)
PlatformTransactionManager implementation for a single Hibernate
SessionFactory. Binds a Hibernate Session from the specified factory
to the thread, potentially allowing for one thread-bound Session per
factory. SessionFactory.getCurrentSession() is required for Hibernate
access code that needs to support this transaction handling mechanism,
with the SessionFactory being configured with SpringSessionContext.
Supports custom isolation levels, and timeouts that get applied as
Hibernate transaction timeouts.
This transaction manager is appropriate for applications that use a
single Hibernate SessionFactory for transactional data access, but it
also supports direct DataSource access within a transaction (i.e.
plain JDBC code working with the same DataSource). This allows for
mixing services which access Hibernate and services which use plain
JDBC (without being aware of Hibernate)! Application code needs to
stick to the same simple Connection lookup pattern as with
DataSourceTransactionManager (i.e.
DataSourceUtils.getConnection(javax.sql.DataSource) or going through a
TransactionAwareDataSourceProxy).
In my java process I'm connecting to MySql using the following spring configuration:
#Configuration
#EnableTransactionManagement
#PropertySources({ #PropertySource("classpath:/myProperties1.properties"), #PropertySource("classpath:/myProperties2.properties") })
public class MyConfiguration {
#Autowired
protected Environment env;
/**
* #return EntityManagerFactory for use with Hibernate JPA provider
*/
#Bean(destroyMethod = "destroy")
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
em.setJpaVendorAdapter(jpaVendorAdapter());
em.setPersistenceUnitManager(persistenceUnitManager());
return em;
}
/**
*
* #return jpaVendorAdapter that works in conjunction with the
* persistence.xml
*/
#Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setDatabase(Database.valueOf(env.getProperty("jpa.database")));
vendorAdapter.setDatabasePlatform(env.getProperty("jpa.dialect"));
vendorAdapter.setGenerateDdl(env.getProperty("jpa.generateDdl", Boolean.class, false));
vendorAdapter.setShowSql(env.getProperty("jpa.showSql", Boolean.class, false));
return vendorAdapter;
}
#Bean
public PersistenceUnitManager persistenceUnitManager() {
DefaultPersistenceUnitManager pum = new DefaultPersistenceUnitManager();
pum.setPackagesToScan("com.app.dal");
pum.setDefaultPersistenceUnitName("my-pu");
pum.setPersistenceXmlLocations("classpath:/META-INF/persistence.xml");
pum.setDefaultDataSource(dataSource());
return pum;
}
#Bean(destroyMethod = "close")
public DataSource dataSource() {
Properties dsProps = new Properties();
dsProps.put("driverClassName", env.getProperty("hikari.driverClassName"));
dsProps.put("username", env.getProperty("hikari.username"));
dsProps.put("password", env.getProperty("hikari.password"));
dsProps.put("jdbcUrl", env.getProperty("hikari.source.data.jdbcUrl"));
dsProps.put("connectionTimeout", env.getProperty("hikari.connectionTimeout", Integer.class));
dsProps.put("idleTimeout", env.getProperty("hikari.idleTimeout", Integer.class));
dsProps.put("maxLifetime", env.getProperty("hikari.maxLifetime", Integer.class));
dsProps.put("maximumPoolSize", env.getProperty("hikari.maximumPoolSize.rtb.source", Integer.class));
dsProps.put("leakDetectionThreshold", env.getProperty("hikari.leakDetectionThreshold", Integer.class));
dsProps.put("jdbc4ConnectionTest", env.getProperty("hikari.jdbc4ConnectionTest", Boolean.class));
HikariConfig config = new HikariConfig(dsProps);
HikariDataSource ds = new HikariDataSource(config);
return ds;
}
#Bean(name = "sourceTxMgr")
public PlatformTransactionManager sourceDatatransactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setPersistenceUnitName("my-pu");
transactionManager.setDataSource(dataSource());
return transactionManager;
}
#Bean
public PersistencyManager persistencyManager() {
return new JpaPersistencyManager();
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
}
The Entity-Manager is injected to the data access layer by the container:
#PersistenceContext(type = PersistenceContextType.TRANSACTION, unitName = "my-pu")
private EntityManager myEntityManager;
And my public business logic methods are annotated with the #Transactional annotation.
As far as I understand the container is responsible for ensuring that the entity-manager returns connections to the pool (in my case HikariCP) once a transaction is done but I did not find any official documentation that describes how the connections are managed. Can anyone explain it to me or provide a good reference that can explain when exactly connections are returned to the pool when using such a configuration?
UPDATE:
The best related info I could come up with so far (taken from here):
The persistence context proxy that implements EntityManager is not the only component needed for making declarative transaction management work. Actually three separate components are needed:
The EntityManager Proxy itself
The Transactional Aspect
The Transaction Manager
Let's go over each one and see how they interact.
The Transactional Aspect
The Transactional Aspect is an 'around' aspect that gets called both before and after the annotated business method. The concrete class for implementing the aspect is TransactionInterceptor.
The Transactional Aspect has two main responsibilities:
At the 'before' moment, the aspect provides a hook point for determining if the business method about to be called should run in the scope of an ongoing database transaction, or if a new separate transaction should be started.
At the 'after' moment, the aspect needs to decide if the transaction should be committed, rolled back or left running.
At the 'before' moment the Transactional Aspect itself does not contain any decision logic, the decision to start a new transaction if needed is delegated to the Transaction Manager.
The Transaction Manager
The transaction manager needs to provide an answer to two questions:
should a new Entity Manager be created?
should a new database transaction be started?
This needs to be decided at the moment the Transactional Aspect 'before' logic is called. The transaction manager will decide based on:
the fact that one transaction is already ongoing or not
the propagation attribute of the transactional method (for example REQUIRES_NEW always starts a new transaction)
If the transaction manager decides to create a new transaction, then it will:
create a new entity manager
bind the entity manager to the current thread
grab a connection from the DB connection pool
bind the connection to the current thread
The entity manager and the connection are both bound to the current thread using ThreadLocal variables.
They are stored in the thread while the transaction is running, and it's up to the Transaction Manager to clean them up when no longer needed.
Any parts of the program that need the current entity manager or connection can retrieve them from the thread. One program component that does exactly that is the EntityManager proxy.
It's not complicated at all.
First, you need to understand that the Spring transaction manager is only a transaction management abstraction. In your case, the actual transactions happen at the JDBC Connection level.
All #Transactional service method calls are intercepted by the TransactionInterceptor Aspect.
The TransactionIntreceptor delegates transaction management to the current configured
AbstractPlatformTransactionManager implementation (JpaTransactionManager in your case).
JpaTransactionManager will bind the current running Spring transaction to an EntityManager, so all DAOs participating in the current transaction share the same Persistence Context.
JpaTransactionManager simply uses the EntityManager Transaction API for controlling transactions:
EntityTransaction tx = txObject.getEntityManagerHolder().getEntityManager().getTransaction();
tx.commit();
The JPA Transaction API simply delegates the call to the underlying JDBC Connection commit/rollback methods.
When the transaction is done (commit/rollback), the org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction calls:
transactionCoordinator().getTransactionContext().managedClose();
which triggers a Hibernate Session (Entity Manager) close.
The underlying JDBC connection is therefore triggered to be closed as well:
jdbcCoordinator.close();
Hibernate has a logical JDBC connection handle:
#Override
public Connection close() {
LOG.tracev( "Closing JDBC container [{0}]", this );
if ( currentBatch != null ) {
LOG.closingUnreleasedBatch();
currentBatch.release();
}
cleanup();
return logicalConnection.close();
}
The logical connection delegates the close call to the currently configured connection provider (DataSourceConnectionProvider in your case), which simply calls the close method on the JDBC connection:
#Override
public void closeConnection(Connection connection) throws SQLException {
connection.close();
}
Like any other connection pooling DataSource, the JDBC connection close simply returns the connection to the pool and doesn't close the physical database connection. That's because the connection pooling DataSource returns a JDBC Connection proxy that intercepts all calls and delegates the closing to the connection pool handling logic.
Note that for RESOURCE_LOCAL transactions, you should also set the hibernate.connection.provider_disables_autocommit property if the autocommit check was disabled by the connection pool. This way, the database connections are going to be acquired lazily prior to executing a SQL query or flushing the Persistence Context.
I'm sure this is a feature, but I would appreciate any pointers/help on the issue.
I'm using Spring (Boot) / JPA (Hibernate) stack.
If I query for an entity (called Homes) using JPA, and increment a Calendar field
homeInstance.getListedDate().add(Calendar.DATE, 1), for example
with no intention of saving this change back to the database (it's only for quick intermediary calculations as the several routines run on a list of these entities).
Then I call a nativequery using an injected EntityManager bean.
#Autowired
EntityManager em;
...
Query nvqry = em.createNativeQuery(...)
nvqry.getResultList()
Doing this automatically persists the changes made to the entity above (which were never supposed to be persisted.
Is there a way to disable this feature without losing the in-memory changes? I manually persist anything I want using the repository, and as such a whole session persistence is useless for me.
That's related to the hibernate FlushMode. If you haven't modified the default configuration, this will be likely set to FlushMode.AUTO.
To alter this behavior you have to provide an EntityManagerFactory bean.
In the configuration pojo you can declare something like this:
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
final DataSource dataSource,
final JpaVendorAdapter jpaVendorAdapter) {
final LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setPersistenceUnitName("domainPC");
factory.setDataSource(dataSource);
factory.setJpaVendorAdapter(jpaVendorAdapter);
factory.setPackagesToScan(getClass().getPackage().getName());
Properties jpaProperties = new Properties();
//and here is your flushMode set.
jpaProperties.setProperty("org.hibernate.flushMode", "COMMIT");
factory.setJpaProperties(jpaProperties);
return factory;
}
I am learning Hibernate now and I need help to understand how Sessions work. I have some methods in a class which I have given below.
I see there is a getCurrentSession() in SessionFactory class. So, it seems that only one Session can be "active" inside a SessionFactory. Is this SessionFactory like
a queue of transactions where the transactions are completed in in order ? If yes, then
is it possible to promote a transaction to a higher or lower priority ?
private static SessionFactory factory;
//Get a hibernate session.
public static Session getSession(){
if(factory == null){
Configuration config = HibernateUtil.getConfiguration();
factory = config.buildSessionFactory();
}
Session hibernateSession = factory.getCurrentSession();
return hibernateSession;
}
public static void commitTransaction(){
HibernateUtil.getSession().getTransaction().commit();
}
public static void rollbackTransaction(){
HibernateUtil.getSession().getTransaction().rollback();
}
And some more methods that use getTransaction().
SessionFactory's job is to hide the session creation strategy. For example, in a web application, you probably want the SessionFactory to return create a Session the first time getCurrentSession() is called on a thread, and then return the same Session from that point forward for the duration of the request. (Since you probably want to load customer data from that session, then maybe modify their account in that same session.) Other times, you may want SessionFactory to create a brand new session every time you call getCurrentSession(). So by hiding this decision behind the SessionFactory API, you simply write code that gets the Session from the factory and operates on it.
The Session is what handles transactions. As you probably expect, transactions are started in a Session, and then either complete or rollback. There is really no way to prioritize them since once they are started, you are committed to either rolling it back or committing it.
Is there any way to remove/suspend a current spring managed hibernate session from a thread so a new one can be used, to then place the original session back onto the thread? Both are working on the same datasource.
To describe the problem in more detail. I'm trying to create a plugin for a tool who has it's own spring hibernate transaction management. In this plugin I would like to do some of my own database stuff which is done on our own spring transaction manager. When I currently try to perform the database actions our transaction manager starts complaining about an incompatibly transactionmanager already being used
org.springframework.transaction.IllegalTransactionStateException: Pre-bound JDBC Connection found! HibernateTransactionManager does not support running within DataSourceTransactionManager if told to manage the DataSource itself. It is recommended to use a single HibernateTransactionManager for all transactions on a single DataSource, no matter whether Hibernate or JDBC access.
A workaround that seems to do the trick is running my own code in a different thread and waiting for it to complete before I continue with the rest of the code.
Is there a better way then that, seems a bit stupid/overkill? Some way to suspend the current hibernate session, then open a new one and afterworths restoring the original session.
Is there any reason you can't have the current transaction manager injected into your plugin code? Two tx managers sounds like too many cooks in the kitchen. If you have it injected, then you should be able to require a new session before doing your work using the #transactional annotation's propagation REQUIRES_NEW attribute see the documentation for an example set-up
e.g.
#transactional(propogation = Propogation.REQUIRES_NEW)
public void addXXX(Some class) {
...
}
But this would use spring's PlatformTransactionManager rather than leaving it up to hibernate to manage the session / transaction.