After two days of debugging our codes on transaction handling, I’m writing this post to share my experience so others don’t have to undergo the same pain and torture. However, this post assumes that you’ve setup your Spring transactions – either via AOP or annotations. If you haven’t done so, here are some good references:
So, once you’ve setup or tried to setup transactions and failed – here are some tips how to debug your transaction management.
Enable logging. In log4j.properties, add log4j.logger.org.springframework.orm.jpa=DEBUG to enable logging of JPA transactions. This will allow you to trace the activities behind the Spring transaction processes. The logs provide detailed information about new, reused and active transactions, transaction modes, transaction commits and more. This is the best way to understand how transaction behaves on your application and gives you an insight how to fix your transactional problems.
Check your database and dialect. Dialects affect transaction handling so be sure to use the appropriate dialect for your database. Note that there are 2 dialects to define – JPA Dialect and Hibernate Dialect. JPA dialect is defined in the EntityManagerFactory while Hibernate is defined in Hibernate properties.
JPADialect is defined in EntityManagerFactory as shown below:
hibernateConfig
While Hibernate Dialect is defined via persistence.xml.
Watch out for listeners, they could mess up your transactions. A couple of examples about listeners (especially ApplicationListener) found online doesn’t consider transaction handling. Most of them performs data updates within the listener. This should not be the case because in doing so, you could be creating a transaction on the listener which forces all other operations to reuse that transaction. The better way of doing it is to call a service or business layer to perform transactional operations. This way, transaction is created only when the service is invoked.
Declare your transaction as readOnly whenever possible. Read-only transactions run faster so enable this on non-writing functions – (e.g. load, read, find, etc). Also, this avoid transaction commits on binded-data. One specific scenario is when Spring validation throws an error and data is still saved despite the validation error, this means that the data was loaded on a non-readOnly transaction and Spring transaction is committing it.
Reduce your transactional method calls. Put all your business logic function calls in one method marked with @Transactional and avoid calling other methods with their own transaction declarations. For example, if your transactions are declared on service layer, avoid calling another service method because this will trigger transactional checks which unnecessarily slows down the system. Call the DAO method directly instead. This way all your operation are contained in a single transaction declaration.
Understand propagation. Transaction propagation is hard to understand because the effects cannot be easily seen in your application… until you start performance tuning or having problems on saved data. Take note that if your transaction propagation is default, it will try to reuse existing transactions. This means that you could lose your transaction settings (e.g. readOnly), because new transaction was not created instead an existing was reused.
11 thoughts on “Troubleshooting Tips: Spring Transactions on JPA”
Thanks for tip! Saved me a lot of time searching for answers.
Hello Allan!
First of all I want to thank you about this tip! Short, but excelent explanations.
Hovewer I have one question regarding “Watch out for listeners, they could mess up your transactions”.
Why transactional methods should not be called from threads like listeners? I thinked that transaction manager must handle such things. I have few threads in my application that calls transactional methods (update db). And I have propagination set to REQUIRES_NEW and readonly=false in these methods.
When these method were not synchronized on java level I often had exceptions like: SQL Error: 1213, SQLState: 40001, Deadlock found when trying to get lock; try restarting transaction.
But I still do not understand why there is no any setting about number of retries of transaction execution and timeout.
I found defaultTimeout setting in the transactionManager, but it not helps in case of deadlock.
Do you have any ideas how it could be solved on JPA level, without java synchronization?
Thank you a lot,
Alexey
Hi Alexey,
Glad you find the post useful. In my experience, transaction declarations in listener changes the original declaration. So, even if you have declared your propagation to “REQUIRES_NEW”, your listener will change it to its own declaration… very hideous way of changing your transactions.
In case you are sure that your transaction propagation is really “REQUIRES_NEW” (through logging) during the time the exception occurs, it could be that you are using static objects in your method or accessing the same object instance in different transactions? Try using Spring to manage your objects…
Cheers,
Allan
Hello Allan!
Actually I am using Spring+JPA+Hibernate and following template to execute update:
public void doUpdate(…) {
EntityManagerFactory entityManagerFactory = jpaTemplate.getEntityManagerFactory();
EntityManager entityManager = null;
try {
entityManager = entityManagerFactory.createEntityManager();
EntityTransaction et = entityManager.getTransaction();
et.begin();
…. update … (could be native or named query)
et.commit();
} finally {
if (entityManager != null) {
entityManager.close();
}
}
}
Threads that calls such methods are timer tasks (could be executed simultaneously) defined as beans in application-servlet.xml.
There is part of xml:
Every bean that operates with database has jpaTemplate parameter.
Methods like above does not have @Transactional definition, but methods that uses persist or merge has @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = false).
I am sorry if there is something wrong in my approach. I am using Spring, JPA and Hibernate few month only.
Thank you,
Alexey.
Hi Alexey,
The doUpdate() method seems fine to me. The problem I can see is that it is invoked from timer tasks – with assumption you are working on a web app. From experience, threads that run as timers do not share the session from the web app. So, all configuration done on your web app does not take effect on the threads.
Hope this helps,
Allan
Hello,
I have a problem. I have the following code:
A Controller Spring MVC, a Service @Transactional and a Dao.
this throw a Session is closed!
Why?
There can be various reasons that causes this.
One common cause is not declaring OpenSessionViewFilter in your web application.
Try searching it in google, lots of references about OpenSessionViewFilter out there.
HTH.
Thank you,
I will try
Useful post. Two issues that need clarification.
“For example, if your transactions are declared on service layer, avoid calling another service method because this will trigger transactional checks which unnecessarily slows down the system.”
Calling method’s in the same service class does not go through the Spring created proxy object and therefore does not incur the cost of transactional checks. So here good OO design should prevail where your have many methods that are small and have a single responsibility. Don’t let the misunderstanding of transaction boundaries cause you to write large methods that do a whole lot of different things.
“Take note that if your transaction propagation is default, it will try to reuse existing transactions. This means that you could lose your transaction settings (e.g. readOnly), because new transaction was not created instead an existing was reused.”
I don’t see the benefit here. First “ReadOnly” transaction setting does not guarantee a read-only transaction. It is dependent on the support for read only connections in the underlying database. All ReadOnly guarantees is that the Hibernate/JPA session will not be flushed. I’ve always configured transaction propagation to REQUIRED for non-read type database calls. So even if there is a nested method call to a method that is marked read-only transaction from a Non-read-only transaction I don’t end up taking the hit with new transactions. And usually if there is an exception I want to rollback the entire transaction anyway.
Hello! I want to say thanks for an interesting site about a subject I have had an interest in for a long time now. I have been lurking and reading the posts avidly so just wanted to express my thanks for providing me with some very good reading material. I look forward to more, and taking a more active part in the discussions here.
I found a link to this blog on Yahoo and really enjoyed it. Thank you!