我有一个注释为的方法@Transactional
。这应该意味着在此方法中触发的任何数据库查询都应该使用相同的事务。但实际上这并没有发生。所发生的情况是,为方法本身打开了一个事务,但是当第一个事务打开时JpaRepository
方法被调用时,会为该特定方法调用打开一个新事务。
为了使事情变得更复杂,对于自定义存储库方法,这个新事务是only打开时JpaRepository
or the JpaRepository custom method
注释为@Transactional
以及。
如果没有,我会得到以下跟踪日志语句:
无需创建交易
[org.springframework.data.jpa.repository.support.SimpleJpaRepository.findFirstByIdNotNull]:
此方法不是事务性的。
因此它不会创建新事务,但它似乎也没有使用调用方法创建的事务。
这是存储库类:
@Repository
public interface LanguageDao extends JpaRepository<Language, Long> {
@Transactional
public Language findByLanguageCode(String languageCode);
public Language findByIdNotNull();
}
这是使用不同存储库方法的方法。
@Transactional
public void afterSingletonsInstantiated() {
languageDao.findByLanguageCode(); //This custom method opens a new transaction, but only because i've annotated this method with @Transactional as well.
languageDao.findAll(); //This one as well because its a standard JpaRepository method.
languageDao.findByIdNotNull();//This custom method doesn't because it lacks its own @Transactional annotation.
}
这是 @Configuration 文件,启用了事务管理和 jpa 存储库
@EnableJpaRepositories(basePackages={"DAOs"}, transactionManagerRef = "customTransactionManager", enableDefaultTransactions = true)
@EnableTransactionManagement
@Configuration
public class RootConfig implements InitializingBean {
@Bean(name = "customTransactionManager")
JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
if (shouldCreateInitialLuceneIndex) {
EntityManager entityManager = entityManagerFactory.createEntityManager();
createInitialLuceneIndex(entityManager);
entityManager.close();
}
return transactionManager;
}
}
相关的application.properties
设置
spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.ImprovedNamingStrategy
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.database-platform = org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.open-in-view = false
一些实际日志。第一行显示该方法的事务afterSingletonsInstantiated
被建造。
[TRACE] 2021-11-08 15:32:40.811 [main] TransactionInterceptor - Getting transaction for [config.StartupChecks$$EnhancerBySpringCGLIB$$134b7631.afterSingletonsInstantiated]
[INFO ] 2021-11-08 15:32:40.815 [main] StartupChecks - Calling sequence table reset procedure
[DEBUG] 2021-11-08 15:32:40.833 [main] SQL - {call RESET_SEQUENCE_TABLE_VALUES_TO_LATEST_ID_VALUES()}
[INFO ] 2021-11-08 15:32:41.087 [main] StartupChecks - Sequence tables reset call finished!
[INFO ] 2021-11-08 15:32:41.087 [main] StartupChecks - doing stuff
[INFO ] 2021-11-08 15:32:41.087 [main] StartupChecks - testing!
[TRACE] 2021-11-08 15:32:41.087 [main] TransactionInterceptor - Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findAll]
[DEBUG] 2021-11-08 15:32:41.088 [main] SQL - select language0_.id as id1_77_, language0_.dateCreated as datecrea2_77_, language0_.englishLanguageName as englishl3_77_, language0_.languageCode as language4_77_, language0_.rightToLeft as righttol5_77_, language0_.translatedLanguageName as translat6_77_ from languages language0_
[TRACE] 2021-11-08 15:32:41.091 [main] TransactionInterceptor - Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findAll]
[INFO ] 2021-11-08 15:32:41.091 [main] StartupChecks - end test!
[TRACE] 2021-11-08 15:32:41.091 [main] TransactionInterceptor - Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findByLanguageCode]
[DEBUG] 2021-11-08 15:32:41.112 [main] SQL - select language0_.id as id1_77_, language0_.dateCreated as datecrea2_77_, language0_.englishLanguageName as englishl3_77_, language0_.languageCode as language4_77_, language0_.rightToLeft as righttol5_77_, language0_.translatedLanguageName as translat6_77_ from languages language0_ where language0_.languageCode=?
[TRACE] 2021-11-08 15:32:41.113 [main] TransactionInterceptor - Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findByLanguageCode]
[TRACE] 2021-11-08 15:32:41.113 [main] TransactionInterceptor - No need to create transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findFirstByIdNotNull]: This method is not transactional.
[DEBUG] 2021-11-08 15:32:41.115 [main] SQL - select authority0_.ID as id1_7_, authority0_.dateCreated as datecrea2_7_, authority0_.NAME as name3_7_ from AUTHORITY authority0_ where authority0_.ID is not null limit ?
[TRACE] 2021-11-08 15:32:41.120 [main] TransactionInterceptor - No need to create transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findFirstByIdNotNull]: This method is not transactional.
这是我已经尝试过的事情的列表。
- 注释
languageDao
与 @Transactional(propagation = Propagation.SUPPORTS) 或
@Transactional(传播=传播.嵌套)。NESTED
hibernate 不支持,因此这会导致错误,即使我将nestedTransactionAllowed 设置为,此错误仍然存在true
在事务管理器上。那个设定SUPPORTS
被忽略。存储库仍然为每个被调用的方法启动一个新事务。 (更新:Propagation.MANDATORY
也没有影响)
- 我已经命名了我的事务管理器
customTransactionManager
并将其作为参数添加到@EnableJpaRepositories,如下所示:@EnableJpaRepositories(basePackages={"DAOs"}, transactionManagerRef = "customTransactionManager")
- 我已经设置了
enableDefaultTransactions
of @EnableJpaRepositories
to false
。这会导致默认方法,例如findAll()
and save()
默认情况下不再在事务中执行。但是,它并不强制他们使用带有注释的调用方法的事务@Transactional
.
所以我的问题是:如何使(自定义)jpa 存储库使用调用方法启动的事务?
编辑:这里JPA - 通过多个 JpaRepository 方法调用跨越事务描述了类似的问题。据用户说,当存储库实现时,spring仅使用现有事务Repository
代替CrudRepository
or JpaRepository
。但这是一个解决方法。
编辑2:当我删除时,我的@Transactional注释继续工作@EnableTransactionManagement
。根据这个帖子当我使用时可能会发生这种情况spring-boot-starter-jdbc
or spring-boot-starter-data-jpa as a dependency
,我就是这么做的。这些依赖关系是否会以某种方式干扰事务管理器的正常工作?