Context
We have a Spring Boot REST application using JPA over Hibernate (Spring Boot 2.0.x, spring-tx 5.0.x, Hibernate 5.2.x). Underlying database is Oracle.
Our architecture doesn't expose JPA entities, instead the service layer converts entities to DTO model classes, which are exposed to the REST controller layer. To make updates, we don't use auto-flushing, but always make an explicit call to service class CRUD methods, which translate to saveAndFlush()
or deleteById()
on the repository (for create/update or delete, respectively).
Objective
What we want is:
- Read-only operations execute non-transactionally
- Write operations (single or multiple) execute within a single transaction
- We are alerted if we forget to declare a transaction on a write operation.
Partial solution
To do this, we have:
Repository super interface is annotated with
@Transactional(propagation=SUPPORTS, readOnly=true)
which covers point 1.Service classes are not annotated, and service write methods are annotated with `@Transactional(propagation=REQUIRED), which covers point 2.
Repository
saveAndFlush(entity)
anddeleteById(id)
methods (the only write methods currently called from services) are annotated with@Transactional(propagation=MANDATORY)
which causes an exception to be thrown if they are executed outside of a transaction context.
So regarding point 3, we are fine if someone defines a new service method that writes data, and forgets to apply @Transactional
on it: an exception will be thrown.
But if someone uses another repository method that writes data, and forgets to annotate it at service level, it will get no transaction from the service level, and execute within the repository's default (class-level) transactionality, that's to say: SUPPORTS
. In this case, with no ongoing transaction, the readOnly flag will be ignored, and the write will be done silently.
Note that Hibernate's flushMode is set to MANUAL when the readOnly
flag is set on @Transactional
. If we had REQUIRED
propagation, the write would be silently ignored (which would not be acceptable either), whereas in our context of SUPPORTS
propagation, the write is silently performed.
Question
Is there any way we can set up Spring transactions / JPA / Hibernate so that our repository permits non-transactional reads, but throws an exception when any data is written, unless via one of the methods that we've overridden to declare @Transactional(propagation=MANDATORY)
?
As noted above, readOnly=true
does not achieve this, even though it does set Hibernate's flushMode.