Home > Blockchain >  Spring @Transactional not working with @NoRepository hierarchy
Spring @Transactional not working with @NoRepository hierarchy

Time:04-29

I have a base interface to contract a query for custom update. This is a library used in other spring applications, and in some enviroments I have a secondary "transactionManager" and also a secondary "entityManager". If this secondary transactionManager exists in spring context, my repository must to use it.

So here is the code of this base repository interface:

@NoRepository
interface MyEntityRepository extends JpaRepository<MyEntity, Integer> {

   @Query("update ....")
   @Modifying
   void updateSomething();

}

I have then two other interfaces to be defined with conditionals:

@Repository("myEntityRepository")
@Transactional(transactionManager = "sharedTransactionManager")
@ConditionalOnBean(name = "sharedTransactionManager")
interface SharedMyEntityRepository extends MyEntityRepository {
}

@Repository("myEntityRepository")
@Transactional
@ConditionalOnMissingBean(name = "sharedTransactionManager")
interface DefaultMyEntityRepository extends MyEntityRepository {
}

But when I run the MyEntityRepository.updateSomething method, I'm getting this error:

org.springframework.dao.InvalidDataAccessApiUsageException: no transaction is in progress; nested exception is javax.persistence.TransactionRequiredException: no transaction is in progress

How can I solve this? Why spring isn't applying the transaction based on the two @Repository (SharedMyEntityRepository or DefaultMyEntityRepository) ?

CodePudding user response:

Because the @Transactional doesn't work backwards ,it can be propagated to the subclasses but not the opposite,you either redefine the method in the subclass or add @Transactional on the parent class or method itself.

CodePudding user response:

After some tests, I got it working.

I just had to contract the @Transactional (without any transactionManager) in the base interface method. After that, at the child repository I had to contract the new transactionManager at my final interface via @Transactional(transactionManager = "sharedTransactionManager").

Here is the code:

@NoRepository
interface MyEntityRepository extends JpaRepository<MyEntity, Integer> {

   @Transactional // this contracts the base method as transactional
   @Query("update ....")
   @Modifying
   void updateSomething();

}

@Repository("myEntityRepository")
@Transactional(transactionManager = "sharedTransactionManager") // override the transactionManager
@ConditionalOnBean(name = "sharedTransactionManager")
interface SharedMyEntityRepository extends MyEntityRepository {
}

@Repository("myEntityRepository")
@ConditionalOnMissingBean(name = "sharedTransactionManager")
interface DefaultMyEntityRepository extends MyEntityRepository {
}

So, when the SharedMyEntityRepository.updateSomething runs, it starts a transaction using the sharedTransactionManager contracted via interface level annotation.

  • Related