我的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中运行(如果有任何意义)
答案 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
}