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

Alf*_* A. 13 java spring hibernate jpa transactions

我有以下场景.我正在使用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;
}
Run Code Online (Sandbox Code Playgroud)

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

此外,如果我使用以下方法分配方法createSampleObjectupdateSampleObject:

@Transactional(propagation = Propagation.NEVER)
Run Code Online (Sandbox Code Playgroud)

这个想法是抛出一个异常,我没有抛出任何异常.问题出在哪儿?分析日志我看到这一行:

org.springframework.orm.jpa.JpaTransactionManager  ==> Creating new transaction with name [com.test.PerformLogic]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT....
Run Code Online (Sandbox Code Playgroud)

这意味着创建了此事务,但我没有看到其他事务的提示.

这是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"/>
Run Code Online (Sandbox Code Playgroud)

JB *_*zet 41

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

A ---> proxy ---> B
Run Code Online (Sandbox Code Playgroud)

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

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

这在文档中有更多细节解释.

  • @AlfredoA.如果您使用AspectJ加载时编织来对代码进行字节编织,则可以避免对上述代理的需求.这样,类A的事务方法foo()可以调用事务方法bar(),也可以调用类A,bar()将在其自己的事务中运行.有关这方面的更多信息,请参阅上面链接的Spring Framework文档. (2认同)

小智 9

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