如何不回滚以前的方法持久化数据?

时间:2014-10-20 13:37:12

标签: java spring jpa jpa-2.1

我的DAO界面很简单:

import org.springframework.transaction.annotation.Transactional;

@Component
@Transactional
public interface TTestDao {

    @Transactional()
    public void first();

    @Transactional(propagation=Propagation.REQUIRES_NEW)
    public void second() ;

}

ad impl方法:

@Override
public void first() {
          entityManager.persist(new TableTest1().setName("data1"));
          this.second();      
}

public void second() {
     entityManager.persist(new TableTest1().setName("data2"));
     throw new RuntimeException(); // it roll backs data2 and data1
}

第一种方法调用第二种方法。第二种方法出错。

目前,如果我拨打first(),所有持久信息都将被回滚。但为什么会这样呢? second()方法在新事务中,我需要在数据库中保留第一个方法的数据。

换句话说,我需要始终保留第一个方法的数据,但只回滚第二个方法数据。我想总是写数据。

我有什么不对吗?

我在SPRING中有这样的db配置:

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"
    p:driverClassName="oracle.jdbc.driver.OracleDriver" p:url="jdbc:oracle:thin:@127.0.0.1:1521:xe"
    p:username="dev" p:password="****" >
</bean>


<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">

    <property name="dataSource" ref="dataSource" />

    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
    </property>

    <property name="jpaProperties">
        <props>
            <prop key="hibernate.hbm2ddl.auto">update</prop>
        </props>
    </property>
</bean>

<context:component-scan base-package="ge.ddrc.transport.persistance.entity">
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository" />
</context:component-scan>


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

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

我的应用程序在tomcat中运行(如果有任何意义)

3 个答案:

答案 0 :(得分:1)

第二种方法仍然存在异常。所以,是的,您创建了2个事务,但由于相同的异常,它们都将被回滚。如果你不想回滚第一个方法,那么你将不得不用try catch包围对第二个方法的调用(这样就不会向Transactional切入点抛出任何异常)。

一般情况下,从另一个事务中调用一个事务并不是一个好主意,因为不清楚事务的范围是什么(这在接口上是不可见的)。因此,如果可能的话,最好分别从前端(或从哪里开始)调用2个方法,并忽略第二个方法中的任何异常。

<强>更新

然而,您案件中的问题完全不同。 Spring事务使用处理事务(和异常)的代理。由于您从first()调用second(),因此完全绕过代理,因此不会创建新事务。

答案 1 :(得分:1)

@StijnGeukens很接近。事实上,Spring确实使用代理来处理事务,而default从另一个事务中调用一个事务方法不会导致创建新的事务。但是,在这种情况下,propogation被专门设置为显式创建require a new事务。应该创建2个交易:

T1: create
    T2: create
    Exception occurs here
    T2: commit
T1: commit

如您所见,异常发生在事务到达其提交点之前。由于它未在First中处理,因此执行将无法到达T1的提交点。这将导致顶级事务被回滚。

但是,您可以在First中处理异常,但仍然无效。来自我上面链接的文档:

Note: Actual transaction suspension will not work out-of-the-box on all transaction managers. This in particular applies to JtaTransactionManager, which requires the javax.transaction.TransactionManager to be made available it to it (which is server-specific in standard J2EE).

因此,propagation=REQUIRES_NEW可能实际上并没有按照它的说法进行操作,因为它不喜欢您的TransactionManager。欢迎来到Spring。

<强>更新 来自JpaTransactionManager文档:

  

在JDBC 3.0上,此事务管理器支持嵌套事务   JDBC 3.0保存点。该   但是,AbstractPlatformTransactionManager.setNestedTransactionAllowed(boolean) "nestedTransactionAllowed"标志默认为“false”   嵌套事务只适用于JDBC连接,而不适用于   JPA EntityManager及其缓存对象。你可以手动设置   如果要使用嵌套事务进行JDBC访问,则标记为“true”   参与JPA事务的代码(假设您的JDBC   驱动程序支持保存点)。请注意,JPA本身不支持   嵌套交易!因此,不要指望JPA访问代码   语义上参与嵌套事务。

这非常令人困惑,但我认为它的含义是,如果设置提到的标志,您将能够使用嵌套事务,使用JDBC 3.0的功能实现。因此,请确保您的驱动程序符合该规范。

答案 2 :(得分:0)

尝试抓住second上的first例外:

@Override
public void first() {
      entityManager.persist(new TableTest1().setName("data1"));
      try {
         this.second();      
      } catch (RuntimeException e) {
        // Do nothing
      }
}

public void second() {
      entityManager.persist(new TableTest1().setName("data2"));
     throw new RuntimeException(); // it roll backs data2
}