1. Overview
In this tutorial, we’re going to examine the cause of the TransactionRequiredException error and how to solve it.
2. TransactionRequiredException
This error typically occurs when we’re trying to perform a database operation that modifies the database without a transaction.
For example, attempting to update a record without a transaction:
Query updateQuery = session.createQuery("UPDATE Post p SET p.title = ?1, p.body = ?2 WHERE p.id = ?3"); updateQuery.setParameter(1, title); updateQuery.setParameter(2, body); updateQuery.setParameter(3, id); updateQuery.executeUpdate();
will raise an exception with a message along the following lines:
... javax.persistence.TransactionRequiredException: Executing an update/delete query at org.hibernate.query.internal.AbstractProducedQuery.executeUpdate(AbstractProducedQuery.java:1586) ...
3. Providing a Transaction
The obvious solution is to wrap any database-modifying operation in a transaction:
Transaction txn = session.beginTransaction(); Query updateQuery = session.createQuery("UPDATE Post p SET p.title = ?1, p.body = ?2 WHERE p.id = ?3"); updateQuery.setParameter(1, title); updateQuery.setParameter(2, body); updateQuery.setParameter(3, id); updateQuery.executeUpdate(); txn.commit();
In the code snippet above, we manually initiate and commit the transaction. Although in a Spring Boot environment, we can achieve this by using the @Transactional annotation.
4. Transaction Support in Spring
If we want more fine-grained control, we can use Spring’s TransactionTemplate. Because this allows the programmer to trigger the persistence of an object immediately before proceeding with the code execution of a method.
For example, let’s say we want to update the post before sending an email notification:
public void update() { entityManager.createQuery("UPDATE Post p SET p.title = ?2, p.body = ?3 WHERE p.id = ?1") // parameters .executeUpdate(); sendEmail(); }
Applying the @Transactional to the method above may cause the email to be sent in spite of an exception in the update process. This is because the transaction will only be committed when the method exits and is about to return to the caller.
Therefore, updating the post within a TransactionTemplate will prevent this scenario as it’ll commit the operation immediately:
public void update() { transactionTemplate.execute(transactionStatus -> { entityManager.createQuery("UPDATE Post p SET p.title = ?2, p.body = ?3 WHERE p.id = ?1") // parameters .executeUpdate(); transactionStatus.flush(); return null; }); sendEmail(); }
5. Conclusion
In conclusion, it’s generally a good practice to wrap database operations in a transaction. It helps in preventing data corruption. The complete source code is available over on Github.