SpringBoot 事务传播 REQUIRES_NEW 不会创建新事务

Posted

技术标签:

【中文标题】SpringBoot 事务传播 REQUIRES_NEW 不会创建新事务【英文标题】:SpringBoot Transactional Propagation REQUIRES_NEW doesn't create new Transaction 【发布时间】:2020-10-20 04:36:41 【问题描述】:

我有以下场景:

@Service
public class ServiceA 

  @Autowired private ServiceB serviceB;

  public void runA()
    serviceB.runB()
  


@Service
public class ServiceB 

  @Autowired private ServiceC serviceC;

  @Transactional
  public void runB()
    serviceC.runC()

    ...rest of the logic
  


@Service
public class ServiceC 

  @Autowired private TestRepository testRepository;

  @Transactional(propagation = Propagation.REQUIRES_NEW)
  public boolean runC()
    Optional<TestEntity> testEntityOptional = testRepository.findByKeyAndType("Key", "Type");
   if(testEntityOptional.isPresent()) 
     testEntityOptional.get().setActive(true);
     return true;
   
   return false;
  


@Transactional
public interface TestRepository
    extends JpaRepository<TestEntity, Integer>, JpaSpecificationExecutor<TestEntity> 

  @Lock(LockModeType.PESSIMISTIC_WRITE)
  @QueryHints(@QueryHint(name = "javax.persistence.lock.timeout", value = "5000"))
  Optional<TestEntity> findByKeyAndType(@NotNull String key, @NotNull String type);

我期待下一个流程:

    ServiceA.runA() 调用 ServiceB.runB() ServiceB.runB() 打开 TRANSACTION_1(如果之前打开过,则使用事务),因为默认传播是 REQUIRED ServiceB.runB() 调用 ServiceC.runC() ServiceC.runC() 打开 TRANSACTION_2 因为传播是 REQUIRED_NEW ServiceC.runC() 调用 TestRepository.findByKeyAndType() 按某些条件获取 testEntity TestRepository.findByKeyAndType() 从数据库返回符合条件的记录并将其锁定以供读取/更新 ServiceB.runC()处理记录 ServiceB.runC() 返回值并提交 TRANSACTION_2 并释放锁 ServiceB.runB() 返回并提交 TRANSACTION_1

但根据我的测试,情况并非如此。它接缝ServiceC.runC() 不创建新事务(TRANSACTION_2),即使设置了传播REQUIRED_NEW(或者它在返回时不提交它),并且仅当ServiceB.runB() 返回时才释放锁定(当提交TRANSACTION_1 时)

有人看到我在这里做错了吗?这是 SpringBoot 的正常行为吗?

锁定超时也不起作用: 我正在使用 Postgress v10 for DB,其中 lock_timeout 设置为“0”。

所以看起来@QueryHints(@QueryHint(name = "javax.persistence.lock.timeout", value = "5000")) 不起作用,因为一旦记录被锁定,另一个尝试读取记录的事务会挂起一段时间,直到记录被解锁

【问题讨论】:

感谢您记录财产。我只是调试并 100% 确定没有打开新事务 【参考方案1】:

启用logging.level.org.springframework.orm.jpa.JpaTransactionManager=DEBUG 后,我看到的就是这个。包括暂停和恢复。

你的事务经理也是JpaTransactionManager吗?

2020-06-30 01:11:53.239 DEBUG 47133 --- [           main] o.s.orm.jpa.JpaTransactionManager        : Creating new transaction with name [com.example.accessingdatajpa.BlogService.save]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2020-06-30 01:11:57.532 DEBUG 47133 --- [           main] o.s.orm.jpa.JpaTransactionManager        : Opened new EntityManager [SessionImpl(1968179698<open>)] for JPA transaction
2020-06-30 01:11:57.538 DEBUG 47133 --- [           main] o.s.orm.jpa.JpaTransactionManager        : Exposing JPA transaction as JDBC [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@7f2542f]

2020-06-30 01:12:02.432 DEBUG 47133 --- [           main] o.s.orm.jpa.JpaTransactionManager        : Found thread-bound EntityManager [SessionImpl(1968179698<open>)] for JPA transaction
2020-06-30 01:12:04.032 DEBUG 47133 --- [           main] o.s.orm.jpa.JpaTransactionManager        : Suspending current transaction, creating new transaction with name [com.example.accessingdatajpa.Service2.save]
2020-06-30 01:12:06.915 DEBUG 47133 --- [           main] o.s.orm.jpa.JpaTransactionManager        : Opened new EntityManager [SessionImpl(2143659352<open>)] for JPA transaction
2020-06-30 01:12:06.916 DEBUG 47133 --- [           main] o.s.orm.jpa.JpaTransactionManager        : Exposing JPA transaction as JDBC [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@42fd8f2f]
2020-06-30 01:12:10.196 DEBUG 47133 --- [           main] o.s.orm.jpa.JpaTransactionManager        : Found thread-bound EntityManager [SessionImpl(2143659352<open>)] for JPA transaction
2020-06-30 01:12:11.489 DEBUG 47133 --- [           main] o.s.orm.jpa.JpaTransactionManager        : Participating in existing transaction

2020-06-30 01:12:12.227 DEBUG 47133 --- [           main] o.s.orm.jpa.JpaTransactionManager        : Initiating transaction commit
2020-06-30 01:12:13.082 DEBUG 47133 --- [           main] o.s.orm.jpa.JpaTransactionManager        : Committing JPA transaction on EntityManager [SessionImpl(2143659352<open>)]
2020-06-30 01:12:13.109 DEBUG 47133 --- [           main] o.s.orm.jpa.JpaTransactionManager        : Closing JPA EntityManager [SessionImpl(2143659352<open>)] after transaction
2020-06-30 01:12:13.110 DEBUG 47133 --- [           main] o.s.orm.jpa.JpaTransactionManager        : Resuming suspended transaction after completion of inner transaction

2020-06-30 01:12:16.409 DEBUG 47133 --- [           main] o.s.orm.jpa.JpaTransactionManager        : Initiating transaction commit
2020-06-30 01:12:17.541 DEBUG 47133 --- [           main] o.s.orm.jpa.JpaTransactionManager        : Committing JPA transaction on EntityManager [SessionImpl(1968179698<open>)]
2020-06-30 01:12:17.542 DEBUG 47133 --- [           main] o.s.orm.jpa.JpaTransactionManager        : Closing JPA EntityManager [SessionImpl(1968179698<open>)] after transaction

【讨论】:

以上是关于SpringBoot 事务传播 REQUIRES_NEW 不会创建新事务的主要内容,如果未能解决你的问题,请参考以下文章

spring中事务传播解读:PROPAGATION_REQUIRES_NEW

Spring事务中requires_new和嵌套传播的区别

Spring事务传播行为REQUIRES_NEW和NESTED用法栗子

Spring事务传播行为REQUIRES_NEW和NESTED用法栗子

spring事务传播行为之使用REQUIRES_NEW不回滚

为啥即使在 Spring 服务类的第二个方法中传播 = Propagation.REQUIRES_NEW 时事务也会回滚?