如何使用 spring aop 定义在循环内部调用的回滚事务?
之前我在 com.TestTransaction 类中创建了一个 updateRecord() 方法。 updateRecord() 方法有一个 for 循环将值插入数据库。如果循环内抛出任何异常,所有插入的值都将回滚。这工作正常,代码如下:
在 java 类文件内部
public class com.TestTransaction{
...
//this is a big transaction
public void updateRecord(){
for(int i=0;i<5;i++){
//insert value to database...
//...if a runtime exception thrown here,
//updateA() method will rollback as a whole transaction,
//so all updates which were done inside the loop will rollback
}
}
...
}
config.xml 文件内部(Spring 配置文件)
<bean id="masterTxManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" >
<property name="dataSource" ref="masterDataSource" />
<property name="nestedTransactionAllowed" value="true" />
</bean>
...
<aop:config proxy-target-class="true">
<aop:pointcut id="testTransactionTX" expression="execution(* com.TestTransaction.*(..))"/>
<aop:advisor pointcut-ref="testTransactionTX" advice-ref="defaultTxAdvice"/>
</aop:config>
...
<tx:advice id="defaultTxAdvice" transaction-manager="masterTxManager">
<tx:attributes>
<tx:method name="update*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
然后我决定在 updateRecord() 方法的循环内编写代码作为单独的方法 doUpdateRecord() 。这样,当 doUpdateRecord() 抛出 RuntimeException 时,它只会回滚此 doUpdateRecord() 并提交所有先前的更新。但似乎无法回滚。
代码如下:
public class com.TestTransaction{
...
//this is no longer a big transaction
public void updateRecord(){
for(int i=0;i<5;i++){
//every doUpdateRecord() call will start a new transaction
doUpdateRecord();
}
}
//this is a transaction
public void doUpdateRecord(){
//insert value to database...
//...if a runtime exception thrown here,
//it only rollback this method
}
}
Spring配置文件:
<bean id="masterTxManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" >
<property name="dataSource" ref="masterDataSource" />
<property name="nestedTransactionAllowed" value="true" />
</bean>
...
<aop:config proxy-target-class="true">
<aop:pointcut id="testTransactionTX" expression="execution(* com.TestTransaction.doUpdateRecord(..))"/>
<aop:advisor pointcut-ref="testTransactionTX" advice-ref="defaultTxAdvice"/>
</aop:config>
...
<tx:advice id="defaultTxAdvice" transaction-manager="masterTxManager">
<tx:attributes>
<tx:method name="*" propagation="REQUIRES_NEW"/>
</tx:attributes>
</tx:advice>
任何人都可以给出发生了什么事情的任何想法吗?当在循环内调用方法(事务)时是否可以回滚一个更新?
Previously I have created a method updateRecord() in com.TestTransaction class. The updateRecord() method has a for loop to insert values into database. If there is any exception thrown inside the loop all the inserted values will rollback. This works fine and code is like below:
Inside the java class file
public class com.TestTransaction{
...
//this is a big transaction
public void updateRecord(){
for(int i=0;i<5;i++){
//insert value to database...
//...if a runtime exception thrown here,
//updateA() method will rollback as a whole transaction,
//so all updates which were done inside the loop will rollback
}
}
...
}
Inside config.xml file (Spring config file)
<bean id="masterTxManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" >
<property name="dataSource" ref="masterDataSource" />
<property name="nestedTransactionAllowed" value="true" />
</bean>
...
<aop:config proxy-target-class="true">
<aop:pointcut id="testTransactionTX" expression="execution(* com.TestTransaction.*(..))"/>
<aop:advisor pointcut-ref="testTransactionTX" advice-ref="defaultTxAdvice"/>
</aop:config>
...
<tx:advice id="defaultTxAdvice" transaction-manager="masterTxManager">
<tx:attributes>
<tx:method name="update*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
Then I decided to make code inside the loop of updateRecord() method as a separate method doUpdateRecord(). So that when doUpdateRecord() throws a RuntimeException, it only rolls back this doUpdateRecord() and all previous updates will be committed. But it seems that it fails to rollback.
Code as below:
public class com.TestTransaction{
...
//this is no longer a big transaction
public void updateRecord(){
for(int i=0;i<5;i++){
//every doUpdateRecord() call will start a new transaction
doUpdateRecord();
}
}
//this is a transaction
public void doUpdateRecord(){
//insert value to database...
//...if a runtime exception thrown here,
//it only rollback this method
}
}
Spring config file:
<bean id="masterTxManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" >
<property name="dataSource" ref="masterDataSource" />
<property name="nestedTransactionAllowed" value="true" />
</bean>
...
<aop:config proxy-target-class="true">
<aop:pointcut id="testTransactionTX" expression="execution(* com.TestTransaction.doUpdateRecord(..))"/>
<aop:advisor pointcut-ref="testTransactionTX" advice-ref="defaultTxAdvice"/>
</aop:config>
...
<tx:advice id="defaultTxAdvice" transaction-manager="masterTxManager">
<tx:attributes>
<tx:method name="*" propagation="REQUIRES_NEW"/>
</tx:attributes>
</tx:advice>
Can anyone give any ideas of what is going on? Is it possible to rollback one update when a method (transaction) is called inside a loop?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
这可能是我在这个问题中遇到的情况。同一类中的调用不会通过代理,并且您的切入点逻辑将被忽略。
It's possibly the case I came across in this question. Calls within the same class don't pass through proxy and your pointcut logic is ignored.
因为 spring 仅支持了解您的方法调用方面,所以 spring 将无法拦截该 doUpdateRecord,因为它不是针对 spring 管理的 bean 实例调用的。为了使方面工作,需要针对 spring bean 实例调用 doUpdateRecord,因为只有这样 spring 才能拦截方法调用并插入事务性内容。
要么将 TestTransaction 的 spring bean 实例注入测试事务本身,并在调用 doUpdateRecord 时引用该实例(不确定这是否有效),要么将 doUpdateRecord 代码移动到另一个 bean 中并调用该 bean。
Because spring only supports aspects at method call that is knows about you spring will not be able to intercept that doUpdateRecord as it was not called against the spring managed instance of the bean. In order for the aspect to work that doUpdateRecord needs to be called against the spring bean instance because only then can spring intercept the method call and insert the transactional stuff.
Either you inject the spring bean instance of TestTransaction into test transaction itself and reference that when you call doUpdateRecord (not sure if this will work) or you move the doUpdateRecord code into another bean and call against that bean.