Propagation.REQUIRES_NEW 不会在 Spring 中使用 JPA 创建新事务

Posted

技术标签:

【中文标题】Propagation.REQUIRES_NEW 不会在 Spring 中使用 JPA 创建新事务【英文标题】:Propagation.REQUIRES_NEW does not create a new transaction in Spring with JPA 【发布时间】:2015-04-13 08:47:36 【问题描述】:

我有以下情况。我正在使用 JPA,Spring:

@Autowired
SampleService service;

@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void PerformLogic(LogicData data) throws SIASFaultMessage

    SampleObject so = createSampleObject();

    try
        .//do some logic to persist things in data
        .
        .
        persistData(data);
        .
        .
        .


        updateSampleObject(so);     
    
    catch(Exception)
        updateSampleObject(so);     
        throw new SIASFaultMessage();
    



@Transactional(propagation = Propagation.REQUIRES_NEW)
public createSampleObject()

    SampleObject so = new SampleObject();

    .
    .//initialize so
    .

    service.persist(so);        
    return so;


@Transactional(propagation = Propagation.REQUIRES_NEW)
public updateSampleObject(SampleObject so)
               
    service.persist(so);        
    return so;

当一切正常时,数据会毫无问题地保存在数据库中。但是,当抛出异常时,我需要方法 updateSampleObject(so) 将信息保存在数据库中。这不是正在发生的事情。如果抛出异常,方法 updateSampleObject 也会回滚,这不是我需要的。我需要这两种方法(createSampleObjectupdateSampleObject)始终保持不变,无论是否有异常被抛出与否。 我怎样才能做到这一点?

此外,如果我将方法 createSampleObjectupdateSampleObject 注释为:

@Transactional(propagation = Propagation.NEVER)

这个想法是抛出异常并且我没有抛出异常。问题出在哪里?分析日志我看到这一行:

org.springframework.orm.jpa.JpaTransactionManager  ==> Creating new transaction with name [com.test.PerformLogic]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT....

这意味着该事务已创建,但我没有看到其他事务的提示。

这是我关于事务的 Spring 配置文件的一部分

<bean id="myDataSource"
      class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="$jdbc.driverClassName"/>
    <property name="url" value="$jdbc.url"/>
    <property name="username" value="$jdbc.username"/>
    <property name="password" value="$jdbc.password"/>
</bean>
<bean id="entityManagerFactory"
      class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="myDataSource"/>
    <property name="packagesToScan" value="cu.jpa"/>
    <property name="persistenceProviderClass" value="org.hibernate.ejb.HibernatePersistence"/>
    <property name="jpaProperties">
        <props>
            <prop key="hibernate.dialect">$hibernate.dialect</prop>
            <prop key="hibernate.show_sql">true</prop>
            <prop key="hibernate.hbm2ddl.auto">$hdm2ddl.auto</prop>
        </props>
    </property>
    <property value="/META-INF/jpa-persistence.xml" name="persistenceXmlLocation"/>
    <property name="persistenceUnitName" value="jpaPersistenceUnit"/>
</bean>

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory"/>
    <property name="nestedTransactionAllowed" value="true" />
</bean>

<tx:annotation-driven transaction-manager="transactionManager"/>

【问题讨论】:

【参考方案1】:

Spring 事务是基于代理的。因此,当 bean A 导致 bean B 的事务性时,它是如何工作的。A 实际上具有对代理的引用,该代理委托给 bean B。该代理是启动和提交/回滚事务的代理:

A ---> proxy ---> B

在您的代码中,A 的事务方法调用 A 的另一个事务方法。因此 Spring 无法拦截调用并启动新事务。这是一个不涉及任何代理的常规方法调用。

所以,如果你想启动一个新事务,方法 createSampleObject() 应该在另一个 bean 中,注入到你当前的 bean 中。

the documentation 对此进行了详细说明。

【讨论】:

谢谢,就是这样。我将仔细研究 Spring 文档。你知道是否有办法让交易不基于代理,而是基于方法? @AlfredoA。如果您使用 AspectJ 加载时编织来对您的代码进行字节编织,这是可能的,从而消除了对上述代理的需求。这样,A 类的事务方法 foo() 可以调用 A 类的事务方法 bar(),bar() 将在自己的事务中运行。有关这方面的更多信息,请参阅上面链接的 Spring Framework 文档。 非常感谢。我在提交事务时进行了很多搜索,因为我有 2 个不同的事务,其中一个依赖于其他的结果。它适用于“Required”和“Requires_New”,但只有当我将它们放入不同的 bean 时。 基于 AspectJ 的事务有什么缺点吗?什么时候应该使用代理以及什么时候使用 AspectJ 样式? 感谢您结束几个小时的挫败感。鲜花应该在路上。【参考方案2】:

我的猜测是,由于这两个方法都在同一个 bean 中,所以 Spring 的 AOP 没有机会拦截 create/updateSampleObject 方法调用。尝试将方法移至单独的 bean。

【讨论】:

非常感谢,就是这样。我会将其标记为正确,但我只是先阅读了另一个。【参考方案3】:

请为同一个类(self)创建一个 bean 并使用 bean.api(需要 requires_new)。 有用。

【讨论】:

以上是关于Propagation.REQUIRES_NEW 不会在 Spring 中使用 JPA 创建新事务的主要内容,如果未能解决你的问题,请参考以下文章

如何在集成测试中使用 Propagation.REQUIRES_NEW 回滚嵌套事务

Spring 事务 REQUIRED 与 REQUIRES_NEW :回滚事务

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

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

Spring在一个事务中开启另一个事务

一文弄懂Spring事务相关知识