如何不回滚以前的方法持久化数据?
Posted
技术标签:
【中文标题】如何不回滚以前的方法持久化数据?【英文标题】:How not to roll back previous methods persist data? 【发布时间】:2014-10-20 13:37:11 【问题描述】:我的 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()
方法在新事务中,我需要将第一个方法的数据保留在数据库中。
换句话说,我需要始终保留第一个方法的数据,但只回滚第二个方法的数据。我想一直写data1。
我有什么问题吗?
我在 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 中运行(如果有任何意义)
【问题讨论】:
【参考方案1】:@StijnGeukens 很接近。 Spring 实际上确实使用代理来处理事务,default 从另一个事务方法中调用一个事务方法不会导致创建新事务。但是,在这种情况下,传播被明确设置为显式 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
。欢迎来到春天。
更新 来自JpaTransactionManager 文档:
在 JDBC 3.0 上,此事务管理器通过以下方式支持嵌套事务 JDBC 3.0 保存点。这 不过,
AbstractPlatformTransactionManager.setNestedTransactionAllowed(boolean) "nestedTransactionAllowed"
标志默认为“false”,因为 嵌套事务只适用于 JDBC 连接,而不适用于 JPA EntityManager 及其缓存对象。您可以手动设置 如果要使用嵌套事务进行 JDBC 访问,请标记为“true” 参与 JPA 事务的代码(前提是您的 JDBC 驱动程序支持保存点)。注意JPA本身不支持 嵌套事务!因此,不要期望 JPA 访问代码 语义上参与嵌套事务。
这非常令人困惑,但我认为它的意思是,如果您设置提到的标志,您将能够使用嵌套事务,使用 JDBC 3.0 的功能实现。因此,请确保您的驱动程序符合该规范。
【讨论】:
感谢您的出色回答!我会给你+1票!我已经更新了我的问题。据我了解,我应该使用 JTA 事务管理器来解决我的问题。是真的吗? p.s 什么是最佳实践,春季哪个事务管理器最好?如何选择?我也在数据库配置中使用aspectj,这也是一种好习惯吗?我是春天的新人。再次感谢! @grep 查看我的更新。只要您设置了适当的标志,您似乎仍然可以使用相同的事务管理器。就您的tx:annotation-driven mode
而言,根据this,ASPECTJ 似乎是合适的选择。
目前我在 XML 配置中使用 JpaTransactionManager。在 java 中,我使用 EntityManager 来处理 db。我应该在哪里声明 AbstractPlatformTransactionManager?我应该将 EntityManager 更改为 AbstractPlatformTransaction Manager 吗?我无法在网络中找到样本...【参考方案2】:
第二种方法仍然抛出异常。所以,是的,您创建了 2 个事务,但由于相同的异常,两个事务都将被回滚。如果您不想回滚第一个方法,那么您必须用 try catch 包围对第二个方法的调用(这样不会向 Transactional 切入点抛出异常)。
一般来说,从另一个事务调用一个事务并不是一个好主意,因为不清楚事务的范围是什么(这在界面上不可见)。因此,如果可能的话,最好从前端(或任何地方)分别调用这两种方法,并忽略那里第二种方法的任何异常。
更新
然而,你的问题是完全不同的。 Spring 事务使用处理事务(和异常)的代理。由于您从 first() 调用 second(),因此您完全绕过了代理,因此永远不会创建新事务。
【讨论】:
try this.second(); catch () - 当我这样做时,它会将所有方法数据 - data1 和 data2 保存在表中。我只想保留 data1。 试试这个更现实的用例:使数据库列名不可为空,然后在持久化 TableTest1 时不要设置名称。还要考虑我帖子中的第二段。 try catch 不能解决问题。这是不正确的。 我的回答(和 jmvivo 的回答)是正确的,但您的问题有不同的原因(请参阅我的更新)。 谢谢你 Stijn。你想写“从未创造过”而不是“曾经创造过”。如果 spring 将创建新事务,一切都会好起来的不是吗?...有什么 spring 解决方案吗?...【参考方案3】:尝试在first
上捕获second
异常:
@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
【讨论】:
没有。它颂歌不回滚“data2”。我只想保存 first() 方法数据。但它会保存所有方法数据。 try this.second(); catch () - 当我这样做时,它会将所有方法数据 - data1 和 data2 保存在表中。我只想保留data1。以上是关于如何不回滚以前的方法持久化数据?的主要内容,如果未能解决你的问题,请参考以下文章