1

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.

wild_nothing
  • 2,845
  • 1
  • 35
  • 47
  • It seems to be related to this [other question](https://stackoverflow.com/questions/39827054/spring-jpa-repository-transactionality) – gtosto Dec 05 '17 at 14:16
  • It is not the same question at all. I searched everywhere for an answer to this question before I raised it. – wild_nothing Dec 05 '17 at 14:18

1 Answers1

2

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

Felipe Mosso
  • 3,907
  • 11
  • 38
  • 61
gtosto
  • 1,381
  • 1
  • 14
  • 18
  • Just to add further input to this, you don't actually need to write Propagation.MANDATORY on the overridden save() method if it's already at the top of the interface in your case. – wild_nothing Dec 06 '17 at 10:10