๐ด The Error You're Seeing
Confirm this matches your console output. If it does, you're in the right place.
jakarta.persistence.TransactionRequiredException: Executing an update/delete query
at org.hibernate.query.sql.internal.NativeQueryImpl.doExecuteUpdate(NativeQueryImpl.java:309)
at org.hibernate.query.sql.internal.NativeQueryImpl.executeUpdate(NativeQueryImpl.java:258)โก Quick Fix Works 80% of the time
Annotate the test method or repository method with @Transactional.
@Test
@Transactional
void testUpdateQuery() {
repo.updateStatus(1L, "ACTIVE");
}๐ง Why this Happens
Tap to expand the deep technical explanation
In a standard `@DataJpaTest`, test methods are `@Transactional` by default and roll back at the end. However, if you call a custom `@Modifying` query, or use the `EntityManager` directly outside of a repository method, Hibernate might execute the query outside of the test's transaction boundary, triggering this error.
The HITEC City Parking Spot Analogy:
It's like trying to sign a legally binding contract during a fire drill. The notary (Hibernate) refuses to stamp the document because you aren't in a secure, formal office environment (Transaction).
๐ How to Reproduce Confirm this is your error
Create a `@DataJpaTest`. Inject `EntityManager`. In the test method, run `entityManager.createNativeQuery("UPDATE users SET name='Deva'").executeUpdate();` without adding `@Transactional` to the specific method.
๐ ๏ธ Solutions (5 Ways to Fix)
Add @Transactional to the test method
๐ Use this if your test method directly executes modifying queries.
Ensure the database modification happens inside a transaction so Hibernate can execute it.
@Test
@Transactional
void testUpdate() {
entityManager.createNativeQuery("UPDATE users SET name='Deva'").executeUpdate();
}Ensure @Modifying is on the repository method
๐ Use this if calling a custom `@Query` from a repository.
Spring Data JPA needs to know the query alters data to wrap it in a transaction automatically.
@Repository
public interface UserRepo extends JpaRepository<User, Long> {
@Modifying
@Transactional
@Query("UPDATE User u SET u.status = :status")
void updateStatus(@Param("status") String status);
}Flush the EntityManager manually
๐ Use this if you are saving entities and want to force the DB to execute the SQL immediately.
Sometimes the query is pending in the persistence context. Flushing sends it to the DB within the existing transaction.
@Test
@Transactional
void testSaveAndFlush() {
repo.save(new User("Deva"));
entityManager.flush(); // Force SQL execution
}Disable Rollback (Commit changes)
๐ Use this if you need the data to persist in the H2 database for subsequent tests in the same class.
By default, `@DataJpaTest` rolls back. If you want the data to stay, you must commit the transaction.
@Test
@Transactional
@Rollback(false) // or @Commit
void testPersistData() {
repo.save(new User("Deva"));
}Use TestEntityManager instead of EntityManager
๐ Use this for idiomatic Spring Boot testing.
Spring Boot provides `TestEntityManager`, which handles transactions and flushing gracefully within tests.
@Autowired
private TestEntityManager testEntityManager;
@Test
void test() {
testEntityManager.persistAndFlush(new User("Deva"));
}๐ Version Notes
Uses javax.persistence.TransactionRequiredException.
Uses jakarta.persistence.TransactionRequiredException.
๐ก๏ธ How to Prevent This Next Time
Use Spring Boot's `TestEntityManager` for JPA tests instead of the standard `EntityManager`. It is designed to work within test transaction boundaries.