javaspringspring-datatransactionalpropagation

Unable to enforce Propagation.MANDATORY on Spring Data CRUD methods


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.


Solution

  • 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