Spring Boot v2.6.4

@Transactional tests are useful to roll back any test data created during tests; however, when a Spring integration test is annotated with @Transactional the following JPA lifecycle callback methods are not executed:

  • @PostPersist
  • @PostLoad
  • @PreUpdate
  • @PostUpdate

Example project with tests demonstrating the issue here:

https://github.com/hughwphamill/transaction-test-entity-issue

I understand this is the desired behavior from a JPA perspective, but it's still undesirable from a test perspective.

@Transactional is incredibly useful in Spring integration tests to roll back test data, while exercising your whole application end to end. If your business logic uses JPA entity listeners then your application can't be properly tested in a @Transactional test.

Comment From: sbrannen

Hi @hughwphamill,

Thanks for opening your first issue for the Spring Framework. 👍🏻

If your business logic uses JPA entity listeners then your application can't be properly tested in a @Transactional test.

You can still test the behavior of your JPA lifecycle callback methods with @Transactional test methods, but you need to take into account the semantics of the unit of work (first level cache) in your ORM tool (i.e., the "persistence context" in JPA terminology).

This usually means that you need to flush the state of the unit of work to the underlying database.

In the Avoid false positives when testing ORM code note in the Testing chapter of the reference manual, we point out that invoking entityManager.flush() will help you to achieve this with JPA.

For your particular test class, you can make the @PostPersist, @PreUpdate, and @PostUpdate tests pass by invoking entityManager.flush() after each invocation of repository.save(entity). To make the @PostLoad test pass, you additionally need to invoke entityManager.clear() after calling entityManager.flush().

In summary, the behavior you have encountered is to be expected since all interactions with the unit of work occur within the same transaction.

To ensure that your JPA lifecycle callback methods are invoked, you have two choices.

  1. Flush and clear the unit of work as described above within a @Transactional test.
  2. Do not use @Transactional tests.

I will add a note to the reference manual to point out the need to flush and clear when using JPA lifecycle callback methods, in case other developers encounter this issue.

Comment From: hughwphamill

@sbrannen Thanks very much for the detailed explanation and for updating the docs!

Comment From: sbrannen

@hughwphamill, you're welcome!

And thanks for providing feedback.