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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28480480/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 06:18:46  来源:igfitidea点击:

Propagation.REQUIRES_NEW does not create a new transaction in Spring with JPA

javaspringhibernatejpatransactions

提问by Alfredo A.

I have the following scenario. I am using JPA, Spring:

我有以下场景。我正在使用 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;
}

When everything works fine, data is persisted in database without problems. However, when an exception is thrown, I need that the method updateSampleObject(so)persist the information in the database. This is not what is happening. If an exception is thrown the method updateSampleObjectgets rolled back also, which is not what I need. I need that these two methods (createSampleObjectand updateSampleObject) get persisted all the time, no matter whether an exception got thrown or not. How can I achieve this?

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

Moreover, if I anotate the methods createSampleObjectand updateSampleObjectwith:

此外,如果我用以下方法注释 createSampleObjectupdateSampleObject方法:

@Transactional(propagation = Propagation.NEVER)

the idea is that an exception is thrown and I get no exception thrown. Where is the problem? Analizing the logs I see this line:

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

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

which means this transaction is created, but I see no hint of the other transaction.

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

This is the part of my configuration file for Spring regarding transactions

这是我关于事务的 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"/>

采纳答案by JB Nizet

Spring transactions are proxy-based. Here's thus how it works when bean A causes a transactional of bean B. A has in fact a reference to a proxy, which delegates to the bean B. This proxy is the one which starts and commits/rollbacks the transaction:

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

A ---> proxy ---> B

In your code, a transactional method of A calls another transactional method of A. So Spring can't intercept the call and start a new transaction. It's a regular method call without any proxy involved.

在你的代码中,A的一个事务方法调用了A的另一个事务方法。所以Spring无法拦截调用并开始一个新的事务。这是一个不涉及任何代理的常规方法调用。

So, if you want a new transaction to start, the method createSampleObject()should be in another bean, injected into your current bean.

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

This is explained with more details in the documentation.

文档中对此进行了更详细的解释。

回答by zmitrok

My guess is that since both methods are in the same bean, the Spring's AOP does not have a chance to intercept the create/updateSampleObject method calls. Try moving the methods to a separate bean.

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

回答by vasu

Please create a bean for the same class(self) and use bean.api(which requires requires_new). It works.

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