Any reason for this?
I've been using Spring data for years but I don't think I've ever unit tested one of their out-of-the-box CRUD methods before.
Why is it that the following interface definition has no effect on their transactional implementation for CRUD methods?
@Repository
@Transactional(propagation = Propagation.MANDATORY)
public interface MyRepository extends JpaRepository<MyEntity, Long> {
Stream<MyEntity> findMyEntityByStatusEquals(Status status);
}
If I call myRepository.save(new MyEntity())
from a test method, WITHOUT my test being wrapped in a transaction, it succeeds.
However, if I call myRepository.findMyEntityByStatusEquals("MY_STATUS")
it fails stating that it needs to be wrapped in a transaction.
The latter case I expect, the former case terrifies me as I don't seem to be able to enforce it to be part of an existing transaction.
::Edit:: Turns out putting @Transactional
at the top of the interface has no effect on Spring Data CRUD methods that have previously been marked as @Transactional
. I always assumed it was also an override when specified on these interfaces.
As stated by documentation here
CRUD methods on repository instances are transactional by default. For reading operations the transaction configuration readOnly flag is set to true, all others are configured with a plain @Transactional so that default transaction configuration applies.
@Transactional has Propagation.REQUIRED as its default propagation type, so when you call the save
method a new transaction just begin.
If you want force Propagation.MANDATORY even on built-in CRUD methods you have to override such methods, i.e
@Repository
@Transactional(propagation = Propagation.MANDATORY)
public interface MyRepository extends JpaRepository<MyEntity, Long> {
Stream<MyEntity> findMyEntityByStatusEquals(Status status);
@Transactional(propagation = Propagation.MANDATORY)
public <MyEntity> MyEntity save(MyEntity entity) {
super.save(entity);
}
}
hope this helps