Spring 为 @Transactional 注解方法中调用的每个 JpaRepository 方法打开一个新事务

2023-12-13

我有一个注释为的方法@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.

这是我已经尝试过的事情的列表。

  1. 注释languageDao与 @Transactional(propagation = Propagation.SUPPORTS) 或 @Transactional(传播=传播.嵌套)。NESTEDhibernate 不支持,因此这会导致错误,即使我将nestedTransactionAllowed 设置为,此错误仍然存​​在true在事务管理器上。那个设定SUPPORTS被忽略。存储库仍然为每个被调用的方法启动一个新事务。 (更新:Propagation.MANDATORY也没有影响)
  2. 我已经命名了我的事务管理器customTransactionManager并将其作为参数添加到@EnableJpaRepositories,如下所示:@EnableJpaRepositories(basePackages={"DAOs"}, transactionManagerRef = "customTransactionManager")
  3. 我已经设置了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,我就是这么做的。这些依赖关系是否会以某种方式干扰事务管理器的正常工作?


这是我试图理解你的问题。我建议启用额外的调试

logging.level.org.springframework.orm.jpa.JpaTransactionManager=DEBUG

我的测试服务类 - 请注意,它被标记为事务性 - 目前这是它放置的唯一位置,因为这就是我们的意图 - 创建事务边界。

@Service
public class LanguageService {

    @Autowired
    private LanguageRepository languageRepository;

    @Transactional
    public void runAllMethods() {
        languageRepository.findByLanguageCode("en");
        languageRepository.findAll();
        languageRepository.findByIdNotNull();
    }

}

接下来是存储库 - 没有事务注释。

public interface LanguageRepository extends JpaRepository<Language, Long> {

    public Language findByLanguageCode(String languageCode);

    public Language findByIdNotNull();

}

现在通过控制器访问服务 - 我得到以下日志。请注意其中显示“使用名称 [com.shailendra.transaction_demo.service.LanguageService.runAllMethods]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT”创建新事务的行 - 意味着该事务是在方法调用开始时创建的。

另请注意“参与现有事务”语句,该语句指示该方法正在参与事务。

2021-11-09 11:43:06.061 DEBUG 24956 --- [nio-8181-exec-1] o.s.orm.jpa.JpaTransactionManager        : Found thread-bound EntityManager [SessionImpl(2084817241<open>)] for JPA transaction
2021-11-09 11:43:06.061 DEBUG 24956 --- [nio-8181-exec-1] o.s.orm.jpa.JpaTransactionManager        : Creating new transaction with name [com.shailendra.transaction_demo.service.LanguageService.runAllMethods]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2021-11-09 11:43:06.069 DEBUG 24956 --- [nio-8181-exec-1] o.s.orm.jpa.JpaTransactionManager        : Exposing JPA transaction as JDBC [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@3107a702]
2021-11-09 11:43:06.069 TRACE 24956 --- [nio-8181-exec-1] o.s.t.i.TransactionInterceptor           : Getting transaction for [com.shailendra.transaction_demo.service.LanguageService.runAllMethods]
2021-11-09 11:43:06.099 TRACE 24956 --- [nio-8181-exec-1] o.s.t.i.TransactionInterceptor           : No need to create transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findByLanguageCode]: This method is not transactional.
Hibernate: select language0_.id as id1_0_, language0_.date_created as date_cre2_0_, language0_.english_language_name as english_3_0_, language0_.language_code as language4_0_, language0_.right_to_left as right_to5_0_, language0_.translated_language_name as translat6_0_ from language language0_ where language0_.language_code=?
2021-11-09 11:43:06.333 DEBUG 24956 --- [nio-8181-exec-1] o.s.orm.jpa.JpaTransactionManager        : Found thread-bound EntityManager [SessionImpl(2084817241<open>)] for JPA transaction
2021-11-09 11:43:06.333 DEBUG 24956 --- [nio-8181-exec-1] o.s.orm.jpa.JpaTransactionManager        : Participating in existing transaction
2021-11-09 11:43:06.333 TRACE 24956 --- [nio-8181-exec-1] o.s.t.i.TransactionInterceptor           : Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findAll]
Hibernate: select language0_.id as id1_0_, language0_.date_created as date_cre2_0_, language0_.english_language_name as english_3_0_, language0_.language_code as language4_0_, language0_.right_to_left as right_to5_0_, language0_.translated_language_name as translat6_0_ from language language0_
2021-11-09 11:43:06.348 TRACE 24956 --- [nio-8181-exec-1] o.s.t.i.TransactionInterceptor           : Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findAll]
2021-11-09 11:43:06.348 TRACE 24956 --- [nio-8181-exec-1] o.s.t.i.TransactionInterceptor           : No need to create transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findByIdNotNull]: This method is not transactional.
Hibernate: select language0_.id as id1_0_, language0_.date_created as date_cre2_0_, language0_.english_language_name as english_3_0_, language0_.language_code as language4_0_, language0_.right_to_left as right_to5_0_, language0_.translated_language_name as translat6_0_ from language language0_ where language0_.id is not null
2021-11-09 11:43:06.348 TRACE 24956 --- [nio-8181-exec-1] o.s.t.i.TransactionInterceptor           : Completing transaction for [com.shailendra.transaction_demo.service.LanguageService.runAllMethods]
2021-11-09 11:43:06.348 DEBUG 24956 --- [nio-8181-exec-1] o.s.orm.jpa.JpaTransactionManager        : Initiating transaction commit
2021-11-09 11:43:06.348 DEBUG 24956 --- [nio-8181-exec-1] o.s.orm.jpa.JpaTransactionManager        : Committing JPA transaction on EntityManager [SessionImpl(2084817241<open>)]

对于只读方法 - 如 findAll - 您会看到“无需创建事务” - 这是因为尽管默认存储库实现“SimpleJpaRepository”被标记为事务性 - 只读方法未标记为事务性。

@Repository
@Transactional(readOnly = true)
public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID> {
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spring 为 @Transactional 注解方法中调用的每个 JpaRepository 方法打开一个新事务 的相关文章

随机推荐