【发布时间】:2021-12-21 10:18:16
【问题描述】:
我有一个用@Transactional 注释的方法。这应该意味着在此方法中触发的任何数据库查询都应该使用相同的事务。但实际上这不会发生。实际情况是为方法本身打开了一个事务,但是当调用第一个 JpaRepository 方法时,会为该特定方法调用打开一个新事务。
为了使事情变得更复杂,对于自定义存储库方法,此新事务仅在 JpaRepository 或 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.
这是我已经尝试过的事情的列表。
- 使用@Transactional(propagation = Propagation.SUPPORTS) 注释
languageDao或 @Transactional(传播 = Propagation.NESTED)。NESTED不受休眠支持,因此这会导致错误,即使我在事务管理器上将 nestedTransactionAllowed 设置为true,此错误仍然存在。设置SUPPORTS被忽略。存储库仍然为每个调用的方法启动一个新事务。 (更新:Propagation.MANDATORY也无效) - 我已将我的事务管理器命名为
customTransactionManager,并将其作为参数添加到 @EnableJpaRepositories,如下所示:@EnableJpaRepositories(basePackages={"DAOs"}, transactionManagerRef = "customTransactionManager") - 我已将
enableDefaultTransactions的@EnableJpaRepositories设置为false。这会导致默认方法(如findAll()和save())不再在默认情况下在事务中执行。但是,它不会强制他们使用带有@Transactional注释的调用方法的事务。
所以我的问题是:如何让(自定义)jpa 存储库使用由调用方法启动的事务?
编辑:这里JPA - Spanning a transaction over multiple JpaRepository method calls 描述了一个类似的问题。根据用户的说法,spring仅在存储库实现Repository而不是CrudRepository或JpaRepository时使用现有事务。但这是一种解决方法。
编辑 2:当我删除 @EnableTransactionManagement 时,我的 @Transactional 注释继续工作。根据this post,当我使用spring-boot-starter-jdbc 或spring-boot-starter-data-jpa as a dependency 时可能会发生这种情况,我就是这样做的。这些依赖是否会以某种方式干扰事务管理器的正常工作?
【问题讨论】:
-
不是说它会有所作为,但是您是否检查过您是否在所有地方都导入了相同的
@Transactional(有两个:JPA 和 Spring 之一)? -
@WimDeblauwe 我刚刚检查过。它使用
org.springframework.transaction.annotation.Transactional之一。 -
我怀疑一切正常,而您只是被启用了 TRACE 日志记录的事实吓倒了。
-
@M.Deinum 你到底是什么意思?我故意通过将
logging.level.org.springframework.transaction.interceptor设置为TRACE来打开TRACE,这样我就可以检查事务是否是由带注释的方法创建的。事实证明是这种情况,但是根据日志,de JpaRepository 方法正在执行相同的操作。他们需要使用调用方法打开的事务,而不是自己启动。 -
对于完整的图片,您应该为
org.springframework.transaction启用调试/跟踪日志记录。您现在只查看日志的单个部分。此外,如果您希望使用customTransactionManager,则需要在@Transactional注释中指定,否则它将使用默认值。另一个提示沟@Repository在您的界面上,它没有任何作用,我也会删除 hte@Transactional(也不会添加任何东西)。
标签: java spring-boot spring-data-jpa transactions