I am in the process of developing a web application and decided that using spring-data would be a good idea. To be fair it makes life extremely easy and the DAO layer very thin.
The issue I am have is to do with persisting data. When I use a JPATransactionManager the persistence works as expected. But I need to use a JTATransactionManager. When I use this it appears that the transaction looks fine but the hibernate flush does not seem to be associated with the tx commit
DEBUG [org.springframework.transaction.jta.JtaTransactionManager] (http--127.0.0.1-8080-2) Creating new transaction with name [com.clubgroups.user.registration.service.impl.UserRegistrationServiceImpl.registerUser]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
DEBUG [org.springframework.transaction.jta.JtaTransactionManager] (http--127.0.0.1-8080-2) Participating in existing transaction
DEBUG [org.springframework.transaction.jta.JtaTransactionManager] (http--127.0.0.1-8080-2) Initiating transaction commit
Above is the debug message that confirms that the transaction is being committed. But the data does not seem to be flushed to the database.
I initialize the transaction manager like this:
#Bean
public JpaTransactionManager transactionManager() {
JtaTransactionManager transactionManager = new JtaTransactionManager();
return transactionManager;
}
Any help would be great.
I am a bit puzzled about your JTA setup as it's highly unusual. First, have a look at the reference documentation on how to setup JTA transactions correctly. Simply instantiating the JtaTransactionManager doesn't work as you need to obtain a UserTransaction from JNDI by some means. You should see an error popping up if you call afterPropertiesSet() in your #Bean method as this will unveil the missing configuration.
Related
Calling JPArepository to deleteAllById Debug Snap Internally using hibernate Provider for JPA
Implementation Class SimpleJpaRepository has Transactional Annotation at the top
Due to Transactional Annotation it went into Proxy Debug Snap
In loop it is deleting in bulk, Deleting one by one in debug mode, when 1 element is deleted checking in database, element still Exist, but then post 1 element deletion forcefully throwing exception, then 1 delete is being committed in database (Any configuration missing here)? see there is some option of Transactional(rollback = "Exception.class"), but how to add it in SimpleJPARepository class ?
Debug Snap
Main issue, I am putting Transactional annotation at top of service method called by rest API, which is internally calling multiple other services which are actual doing JPA transaction like update/delete/create (So transactional annotation on top of my service is not going in Proxy as per step 2 debug (But internal JPA calls going into Proxy), checked many things, public method...) still nothing worked
StartupClass Configuration Annotation
#SpringBootApplication
#ImportResource("classpath:spring/starter.xml")
#PropertySource("classpath:config/${env}/application.properties")
#EnableJpaAuditing()
#EnableTransactionManagement
#EnableJpaRepositories(repositoryFactoryBeanClass = EnversRevisionRepositoryFactoryBean.class)
Transaction Manager Main
#Bean()
#Primary
public PlatformTransactionManager transactionManager(EntityManagerFactory emf) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
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.
I have a little of confusion about JDBC connection, transactions and their integration in EJB, JTA, Hibernate environment. My doubts are:
when we use #Resource DataSource ds; ... ds.getConnection() , are we working in the same transaction used by the managed bean? Should we close the connection, statement, resultset?
what about session.doWork? Are we in the same transaction? What about closing statement and result set?
aggressive release mode in Hibernate means that connections are closed after each statement. Does it mean that transaction is committed too? (I don't think this is true, but I can't understand how Hibernate works here)
There are a few things you need to figure out. First thing you need to identify what is your unit of work.
The session-per-request pattern is one of the most used and unless you have specific needs stick with that.
If you are using Hibernate you don't use statements and result sets directly. Hibernate will do that for you. what you need to close is the hibernate session
What you use is a SessionFactory and a Session object. The session pretty much represents your unit of work. Inside the hibernate session you get your objects, you change them and save them back.
The session per request pattern opens a session when a request is received and closes it when the response is sent back.
In a container managed EJB session bean a transaction is available and the datasource you (or hibernate) use in such a container is automatically handled by a JTA TransactionManager.
Now because Hibernate is smart it can automatically bind the "current" Session to the current JTA transaction.
This enables an easy implementation of the session-per-request strategy with the getCurrentSession() method on your SessionFactory:
try {
UserTransaction tx = (UserTransaction)new InitialContext()
.lookup("java:comp/UserTransaction");
tx.begin();
// Do some work
factory.getCurrentSession().load(...);
factory.getCurrentSession().persist(...);
tx.commit();
}
catch (RuntimeException e) {
tx.rollback();
throw e; // or display error message
}
So to answer your questions:
If you are using Hibernate with JTA in a container you'd be better off using a JPA EntityManager or maybe spring hibernate template.
Here are some references:
http://community.jboss.org/wiki/sessionsandtransactions#Transaction_demarcation_with_JTA
http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/orm/hibernate3/HibernateTemplate.html
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.
We are looking into using the JdbcTemplate for accessing the DB - but we have many different DB-connections, that each class could use, so injecting the jdbcTemplate is not an option atm. So if we do a
jdbcTemplate = new JdbcTemplate(dataSource);
what will the transaction policy be? Auto-commit is off in the DB.
You can configure each javax.sql.DataSource object to enable auto-commit if that does the job, or disable auto-commit and write the transaction logic programatically.
Both the java.sql.Connection and the javax.sql.DataSource class have methods for enable/disable auto-commit.
Regarding dependency injection and Spring, you can still inject a datasource object into your repository. If you also let each repository extend the org.springframework.jdbc.core.support.JdbcDaoSupport class, then you have a JdbcTemplate object available for you with the derived getJdbcTemplate() method.
You can also let Spring handle the transaction handling for you. Without a XA transaction manager, you need one transaction manager for each datasource. With many transaction managers, declarative transaction support with the #Transactional annotation is impossible. You can however, inject the transaction manager into your service class. This is described in the reference documentation here.