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.