[英]Java Spring @Transactional method not rolling back as expected
下面是我正在嘗試做的快速概述。 我想從一個方法調用將記錄推送到數據庫中的兩個不同的表。 如果有任何失敗,我希望一切都回滾。 因此,如果insertIntoB
失敗,我想要回滾insertIntoA
提交的任何內容。
public class Service {
MyDAO dao;
public void insertRecords(List<Record> records){
for (Record record : records){
insertIntoAAndB(record);
}
}
@Transactional (rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public void insertIntoAAndB(Record record){
insertIntoA(record);
insertIntoB(record);
}
@Transactional(propagation = Propagation.REQUIRED)
public void insertIntoA(Record record){
dao.insertIntoA(record);
}
@Transactional(propagation = Propagation.REQUIRED)
public void insertIntoB(Record record){
dao.insertIntoB(record);
}
public void setMyDAO(final MyDAO dao) {
this.dao = dao;
}
}
MyDAO dao
是使用MyDAO dao
映射到數據庫的接口,使用Spring注入設置。
現在,如果insertIntoB
失敗, insertIntoB
所有insertIntoA
仍然會被推送到數據庫。 我該如何糾正這種行為?
編輯:
我修改了這個類,以便更准確地描述我想要實現的目標。 如果我直接運行insertIntoAAndB
,如果有任何問題,回滾就會起作用,但是如果我從insertRecords
調用insertIntoAAndB
,則如果出現任何問題,則回滾不起作用。
我找到了解決方案!
顯然Spring不能攔截對事務方法的內部方法調用。 所以我拿出了調用事務方法的方法,並把它放到一個單獨的類中,並且回滾工作得很好。 下面是修復的一個粗略示例。
public class Foo {
public void insertRecords(List<Record> records){
Service myService = new Service();
for (Record record : records){
myService.insertIntoAAndB(record);
}
}
}
public class Service {
MyDAO dao;
@Transactional (rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public void insertIntoAAndB(Record record){
insertIntoA(record);
insertIntoB(record);
}
@Transactional(propagation = Propagation.REQUIRED)
public void insertIntoA(Record record){
dao.insertIntoA(record);
}
@Transactional(propagation = Propagation.REQUIRED)
public void insertIntoB(Record record){
dao.insertIntoB(record);
}
public void setMyDAO(final MyDAO dao) {
this.dao = dao;
}
}
我認為您遇到的行為取決於您正在使用的ORM /持久性提供程序和數據庫。 我使用hibernate和mysql測試了你的案例,所有的事務都回滾了。
如果您確實使用hibernate啟用SQL和事務日志記錄來查看它正在做什么:
log4j.logger.org.hibernate.SQL=DEBUG
log4j.logger.org.hibernate.transaction=DEBUG
// for hibernate 4.2.2
// log4j.logger.org.hibernate.engine.transaction=DEBUG
如果您使用普通的jdbc(使用spring JdbcTemplate),您還可以在Spring級別調試SQL和事務
log4j.logger.org.springframework.jdbc.core=DEBUG
log4j.logger.org.springframework.transaction=DEBUG
仔細檢查你的自動提交設置和數據庫特定的peciular(例如:大多數DDL將立即進行調試,盡管spring / hibernate這樣做,你將無法回滾)
僅僅因為jdk不僅使用方法解析aop注釋,還使用目標類解析注釋。 例如,你有方法A和@transactional,方法B調用方法A但沒有@transactional,當你用反射調用方法B時,Spring AOP會檢查B方法,目標類有任何注釋。 因此,如果此類中的調用方法不是@transactional,則它不會解析此方法中的任何其他方法。 最后,向您展示源代碼:org.springframework.aop.framework.jdkDynamicAopProxy.class
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
......
// Get the interception chain for this method.
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// Check whether we have any advice. If we don't, we can fallback on direct
// reflective invocation of the target, and avoid creating a MethodInvocation.
if (chain.isEmpty()) {
// We can skip creating a MethodInvocation: just invoke the target directly
// Note that the final invoker must be an InvokerInterceptor so we know it does
// nothing but a reflective operation on the target, and no hot swapping orfancy proxying.
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
}
else {
// We need to create a method invocation...
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
retVal = invocation.proceed();
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.