@Transactional(propagation = Propagation.REQUIRES_NEW) 不会创建新的事务,而是参与之前的事务。

3

春天总是为REQUIRES_NEW创建新的事务,无论是否存在已有事务。但是,在哪种情况下Spring不会创建新的事务,而是参与现有事务呢?

以下是不会创建新事务,而是参与现有事务的情况。

所有方法,mainMethod、methodA()、methodB()和logExceptionMethod()都在不同的bean中, 理想情况下,当methodB()失败时,异常将在catch块中捕获,并且logExceptionMethod()将通过创建新事务来审计日志。

但是在这里,即使logExceptionMethod()方法被标注为requires_new并且是公共的且位于不同的bean中,也没有启动新的事务,而是参与现有事务,导致回滚。

mainMethod - Propagation.REQUIRES methodA - Propagation.REQUIRES_NEW methodB - 未标注 logExceptionMethod - Propagation.REQUIRES_NEW

流程

    @Transactional
    mainMethod() {
          try{
                methodA();
           } catch(Exception e) {
                   throw e;
           }
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    methodA(){
         try{
               methodB()
              }
         catch(Exception e) {
              throw e;
          }
    }
    
    methodB(){
          try {
                 // api call gives exception
                }
           catch(Exception e) {
                logExceptionMethod();
                throw e;
            }
    }
    
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    logExceptionMethod() {
          // create log entity
          // store entity in db
    }

日志

DEBUG o.s.orm.jpa.JpaTransactionManager - 1F2EA187-C884-461C-8183-328A4B7621D0 - Creating new transaction with name [mainMethod]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
        
DEBUG o.s.orm.jpa.JpaTransactionManager - 1F2EA187-C884-461C-8183-328A4B7621D0 - Exposing JPA transaction as JDBC [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@53f498b2]
        
TRACE o.s.t.i.TransactionInterceptor - 1F2EA187-C884-461C-8183-328A4B7621D0 - Getting transaction for [mainMethod]
        
TRACE o.s.d.r.c.s.TransactionalRepositoryProxyPostProcessor$RepositoryAnnotationTransactionAttributeSource - 1F2EA187-C884-461C-8183-328A4B7621D0 - Adding transactional method 2023-08-11 03:04:13 [qtp1482177069-1382] 
        
TRACE o.s.b.f.s.DefaultListableBeanFactory - 1F2EA187-C884-461C-8183-328A4B7621D0 - Returning cached instance of singleton bean 'transactionManager'
        
DEBUG o.s.orm.jpa.JpaTransactionManager - 1F2EA187-C884-461C-8183-328A4B7621D0 - Found thread-bound EntityManager [SessionImpl(1543398267<open>)] for JPA transaction
        
INFO - 1F2EA187-C884-461C-8183-328A4B7621D0 - Current transaction : mainMethod
        
DEBUG o.s.orm.jpa.JpaTransactionManager - 1F2EA187-C884-461C-8183-328A4B7621D0 - Found thread-bound EntityManager [SessionImpl(1543398267<open>)] for JPA transaction
        
DEBUG o.s.orm.jpa.JpaTransactionManager - 1F2EA187-C884-461C-8183-328A4B7621D0 - Suspending current transaction, creating new transaction with name [methodA]
        
DEBUG o.s.orm.jpa.JpaTransactionManager - 1F2EA187-C884-461C-8183-328A4B7621D0 - Opened new EntityManager [SessionImpl(1186799888<open>)] for JPA transaction
        
        
DEBUG o.s.orm.jpa.JpaTransactionManager - 1F2EA187-C884-461C-8183-328A4B7621D0 - Exposing JPA transaction as JDBC [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@54c9937c]
        
TRACE o.s.t.i.TransactionInterceptor - 1F2EA187-C884-461C-8183-328A4B7621D0 - Getting transaction for [methodA]
        
INFO- 1F2EA187-C884-461C-8183-328A4B7621D0 - Current transaction : methodA
        
ERROR - 1F2EA187-C884-461C-8183-328A4B7621D0 - Exception Occured 
        
TRACE o.s.d.r.c.s.TransactionalRepositoryProxyPostProcessor$RepositoryAnnotationTransactionAttributeSource - 1F2EA187-C884-461C-8183-328A4B7621D0 - Adding transactional method 'org.springframework.data.jpa.repository.support.SimpleJpaRepository.save' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
        
TRACE o.s.b.f.s.DefaultListableBeanFactory - 1F2EA187-C884-461C-8183-328A4B7621D0 - Returning cached instance of singleton bean 
        'transactionManager'
        
DEBUG o.s.orm.jpa.JpaTransactionManager - 1F2EA187-C884-461C-8183-328A4B7621D0 - Found thread-bound EntityManager [SessionImpl(1186799888<open>)] for JPA transaction
        
DEBUG o.s.orm.jpa.JpaTransactionManager - 1F2EA187-C884-461C-8183-328A4B7621D0 - Participating in existing transaction
        
INFO  - 1F2EA187-C884-461C-8183-328A4B7621D0 - Added entry in log
        
TRACE o.s.t.i.TransactionInterceptor - 1F2EA187-C884-461C-8183-328A4B7621D0 - Completing transaction for [methodA] after exception: 
        
DEBUG o.s.orm.jpa.JpaTransactionManager - 1F2EA187-C884-461C-8183-328A4B7621D0 - Initiating transaction rollback
        
DEBUG o.s.orm.jpa.JpaTransactionManager - 1F2EA187-C884-461C-8183-328A4B7621D0 - Rolling back JPA transaction on EntityManager [SessionImpl(1186799888<open>)]
        
DEBUG o.s.orm.jpa.JpaTransactionManager - 1F2EA187-C884-461C-8183-328A4B7621D0 - Closing JPA EntityManager [SessionImpl(1186799888<open>)] after transaction
        
DEBUG o.s.orm.jpa.JpaTransactionManager - 1F2EA187-C884-461C-8183-328A4B7621D0 - Resuming suspended transaction after completion of inner transaction
        
TRACE o.s.t.i.TransactionInterceptor - 1F2EA187-C884-461C-8183-328A4B7621D0 - Completing transaction for [mainMethod] after exception:
        
DEBUG o.s.orm.jpa.JpaTransactionManager - 1F2EA187-C884-461C-8183-328A4B7621D0 - Initiating transaction rollback
        
DEBUG o.s.orm.jpa.JpaTransactionManager - 1F2EA187-C884-461C-8183-328A4B7621D0 - Rolling back JPA transaction on EntityManager [SessionImpl(1543398267<open>)]
        
DEBUG o.s.orm.jpa.JpaTransactionManager - 1F2EA187-C884-461C-8183-328A4B7621D0 - Not closing pre-bound JPA EntityManager after transaction


现在我可以使用特定的noRollbackFor属性来防止事务回滚,但是主要问题仍然是:
1. 为什么Spring没有创建新的事务? 2. 在什么情况下会发生这种情况?
4个回答

3
为什么没有创建新的事务?
问题出在只有在创建代理bean时才会应用事务属性。在这种情况下,似乎没有创建代理,导致没有新的事务。
日志显示,在包含HelperC的类中,没有为logExceptionMethod方法创建代理。
(使用ApplicationContext.getBean(className).getClass().getName()打印)
INFO 6F28649F-35D3-4382-9BA8-1AEAFA6E5FF3 ###classname: HelperC

然而,CGLIB代理是为MainClass和SecondClass类创建的,其中包含mainMethod和methodA方法。
INFO 8CBCB131-A16B-4F0D-9C17-AED5E15303CE - ###classname: MainClass$$EnhancerBySpringCGLIB$$b32bbff5
INFO 6F28649F-35D3-4382-9BA8-1AEAFA6E5FF3 - ###classname: SecondClass$$EnhancerBySpringCGLIB$$4c237172

为什么代理未能创建?

日志表明,HelperC这个bean不符合所有BeanPostProcessor的处理要求,具体来说,它不符合自动代理的要求。

INFO o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker - Bean 'helperA' of type [HelperA] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
INFO o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker - Bean 'helperB' of type [HelperB] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
INFO o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker - Bean 'helperC' of type [HelperC] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)

这可能是由于几个原因造成的: HelperC被认为是一个基础设施bean,它是Spring框架本身使用的低级别bean。 注册的bean后处理器的数量(beanFactory.getBeanPostProcessorCount())小于某个目标数量。 HelperC不是BeanPostProcessor的实例。 这是由PostProcessorRegistrationDelegate$BeanPostProcessorChecker类确定的。
此外,methodSecurityConfig类扩展了下面的类,Configuration不允许创建代理,并将其定义为低级别bean(基础设施bean)。
@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class GlobalMethodSecurityConfiguration

这个方法阻止了代理的创建。methodSecurityConfig类自动装配HelperA,而HelperA又自动装配HelperB,最终导致包含logExceptionMethod的类HelperC不会被代理。

为什么在我的情况下methodSecurityConfig中使用了HelperA?

由于一些业务逻辑要求,methodSecurityConfig中使用了HelperA。结果是,HelperA的methodA被自动装配,从而导致HelperB和随后包含logExceptionMethod的HelperC被自动装配。然而,Spring Security配置阻止了对这些类的代理创建。

解决方案:

为了解决这个问题,我按照以下步骤进行操作:

  1. 移除对具有logExceptionMethod的HelperA的依赖:

由于HelperA触发了导致logExceptionMethod自动装配链的问题,我尝试从methodSecurityConfig中移除对HelperA的依赖。这将打破链条,并可能允许为相关类创建代理。

  1. 使用JPARepository类来满足安全需求,而不是使用HelperA(根据业务用例可能会有所不同)。
或者,我也可以将logExceptionMethod方法转移到其他未在安全上下文中使用的类中,因为默认情况下,在安全上下文中使用的bean是不允许创建代理的。
总之,负责为logExceptionMethod方法创建事务的bean没有被代理。这是因为它被自动装配到安全上下文中,并且父类中的配置限制了在此上下文中创建代理的操作。

https://synyx.de/blog/bean-x-of-type-y-is-not-eligible-for-getting-processed-by-all-beanpostprocessors/

https://www.baeldung.com/spring-not-eligible-for-auto-proxying

https://github.com/spring-projects/spring-framework/blob/main/spring-context/src/main/java/org/springframework/context/support/PostProcessorRegistrationDelegate.java#L412


0
简单来说,我的交易失败是因为有人已经使用了我的信息,然而由于这些信息实际上是属于我自己的,所以交易被标记并可能阻止其他人使用我的信息,并将我的信息应用到正确的账户上!我开始学习技术,是因为我无法理解为什么有人可以使用我的信息并阻止我使用我的账户!这是隧道尽头的一线希望!还有希望!!我已经努力解决这个问题多年了!

不好意思,但我不明白你的回答如何帮助到楼主(或其他遇到类似问题的人)。 - Abra

0
这是一个正常的情况,为什么您的日志没有保存,因为Spring框架的事务基础设施代码在运行时、未检查的异常情况下将事务标记为仅回滚。
我认为您应该使用一个Transactional参数。
@Transactional(noRollbackFor="YourException.class")

而且我建议你检查一下这个one

根据文档:PROPAGATION_REQUIRES_NEW为给定作用域启动一个新的、独立的“内部”事务。该事务将完全独立于外部事务进行提交或回滚,具有自己的隔离范围、自己的锁集等。外部事务将在内部事务开始时被挂起,并在内部事务完成后继续运行。 - Andrei Lisa

0
Spring要求你抛出一个异常,以便被Spring框架捕获,从而实现自动事务回滚。如果你手动捕获异常,就需要手动进行回滚操作,你可以将其添加到异常处理中。
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

你还需要将它添加到交易中。
@Transactional(rollbackFor = Exception.class);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接