You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

@Transactional方法内JpaRepository未复用外层事务该如何解决

问题背景

我有一个标注了@Transactional的方法,按常理该方法内触发的所有数据库查询都应该使用同一个事务,但实际运行并未符合预期:方法本身成功开启了事务,但调用第一个JpaRepository方法时,就会为该方法单独开启新事务。
更复杂的是,自定义Repository方法只有自身也标注@Transactional时才会开启新事务,否则会输出如下Trace日志:

No need to create transaction for
[org.springframework.data.jpa.repository.support.SimpleJpaRepository.findFirstByIdNotNull]:
This method is not transactional.

此时既不会创建新事务,也不会复用调用方方法创建的事务。


相关代码

Repository类代码

@Repository
public interface LanguageDao extends JpaRepository<Language, Long> {

    @Transactional
    public Language findByLanguageCode(String languageCode);

    public Language findByIdNotNull();

}

业务调用方法代码

@Transactional
public void afterSingletonsInstantiated() {
    languageDao.findByLanguageCode(); //该自定义方法会开新事务,原因是自身标注了@Transactional
    languageDao.findAll(); //标准JpaRepository方法也会开新事务
    languageDao.findByIdNotNull();//该自定义方法无@Transactional注解,不会开新事务
}

事务&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(propagation = Propagation.NESTED):NESTED不被Hibernate支持会报错,即便是给事务管理器设置nestedTransactionAllowed为true也无效;SUPPORTS配置被忽略,Repository调用时仍会开启新事务。补充:Propagation.MANDATORY也无效果。
  • 将事务管理器命名为customTransactionManager,并在@EnableJpaRepositories中配置transactionManagerRef = "customTransactionManager"。
  • 将@EnableJpaRepositories的enableDefaultTransactions设置为false:此时findAll()、save()等默认方法不再默认开启事务,但也不会复用调用方@Transactional标注方法开启的事务。

补充信息&疑问

  • 有类似场景的开发者反馈,Repository直接继承Repository而非CrudRepository/JpaRepository时Spring会复用现有事务,但这只是临时方案。
  • 移除@EnableTransactionManagement后@Transactional注解仍然生效,这是引入spring-boot-starter-jdbc或者spring-boot-starter-data-jpa依赖后的常见现象,不确定这些依赖是否会干扰事务管理器的正常工作。
  • 如何让自定义和默认的JpaRepository方法都复用调用方开启的事务?

内容的提问来源于stack exchange,提问作者Maurice

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 10:39:00