Spring 中的 UnexpectedRollbackException 与 AOP @AfterReturning
UnexpectedRollbackException in Spring with AOP @AfterReturning
我使用 Spring AOP 为每个保存操作设置了一个日志记录,我在其中标记了 @Transactional
。问题是当我的保存方法抛出异常并将事务标记为仅回滚时,但使用 AOP 的日志记录操作不知道。这就是为什么会导致:
'Transaction silently rolled back because it has been marked as rollback-only'.
如何克服这种情况?
我的保存方式:
@Transactional
public void create(SecModuleRequest secModuleRequest) {
SecModule secModule = secModuleRepository.save(pData); // throw data integrity exception
}
我的记录方式:
@Transactional
@AfterReturning(value = "execution(public * save(..)) && this(org.springframework.data.repository.CrudRepository)", returning = "responseEntity")
public void onSaveExecuted(JoinPoint pjp, Object responseEntity) {
try {
...
insertAuditLog(jsonStr, entityActionLog.getQueryClauseExt());
} catch (Exception ex) {
ex.printStackTrace();
}
}
public int insertAuditLog(String auditContent, String queryRowId) {
String sql = " Insert into LOG (LOG_ID,LOG_DATETIME, LOG_CONTENT) " +
" values (LOG_SEQ.NEXTVAL, SYSDATE, ?) ";
Query query = entityManager.createNativeQuery(sql);
query.setParameter(1, auditContent);
int resultInsert = query.executeUpdate();
return resultInsert;
}
我认为这里的方法调用顺序如下
方法create()
启动一个事务并调用方法secModuleRepository.save(pData)
。
假设 secModuleRepository.save(pData)
有默认的交易传播 REQUIRED
,它参与了以方法 create()
开始的交易。
save()
returns 无一例外地得到 @AfterReturning
建议方法 onSaveExecuted()
的建议。
此事务将在提交事务时导致 DataIntegrityViolationException
。这发生在事务结束时,也就是控件退出方法 create()
.
时
要记录所有情况下的 save()
方法调用,建议类型应为 @After
或 @Around
并且日志调用不应是事务的一部分提交时回滚。
以下代码使用 TransactionTemplate
启动新事务并插入日志。这个想法是要记录一个新事务,并且给出的示例代码重用了与问题共享的代码,可以根据要求进行改进。
@Aspect
@Component
public class TxnAspect {
@PersistenceContext
private EntityManager entityManager;
// single TransactionTemplate shared amongst all methods in this instance
private final TransactionTemplate transactionTemplate;
// use constructor-injection to supply the PlatformTransactionManager
public TxnAspect(PlatformTransactionManager transactionManager) {
this.transactionTemplate = new TransactionTemplate(transactionManager);
}
@Around(value = "execution(public * save(..)) && this(org.springframework.data.jpa.repository.JpaRepository)")
public Object onSaveExecuted(ProceedingJoinPoint pjp) throws Throwable {
Object responseEntity = null;
try {
responseEntity = pjp.proceed();
} finally {
//gather the details and log
executeInNewTxn("log1","log2");
}
return responseEntity;
}
public Object executeInNewTxn(String str1, String str2) {
transactionTemplate.setPropagationBehavior(Propagation.REQUIRES_NEW.value());
return transactionTemplate.execute(new TransactionCallback<Object>() {
// the code in this method executes in a transactional context
public Object doInTransaction(TransactionStatus status) {
return insertAuditLog(str1, str2);
}
});
}
public int insertAuditLog(String auditContent, String queryRowId) {
String sql = " Insert into LOG (LOG_ID,LOG_DATETIME, LOG_CONTENT) "
+ " values (100, SYSDATE, ?) ";
Query query = entityManager.createNativeQuery(sql);
query.setParameter(1, auditContent);
int resultInsert = query.executeUpdate();
return resultInsert;
}
}
希望对您有所帮助
我使用 Spring AOP 为每个保存操作设置了一个日志记录,我在其中标记了 @Transactional
。问题是当我的保存方法抛出异常并将事务标记为仅回滚时,但使用 AOP 的日志记录操作不知道。这就是为什么会导致:
'Transaction silently rolled back because it has been marked as rollback-only'.
如何克服这种情况?
我的保存方式:
@Transactional
public void create(SecModuleRequest secModuleRequest) {
SecModule secModule = secModuleRepository.save(pData); // throw data integrity exception
}
我的记录方式:
@Transactional
@AfterReturning(value = "execution(public * save(..)) && this(org.springframework.data.repository.CrudRepository)", returning = "responseEntity")
public void onSaveExecuted(JoinPoint pjp, Object responseEntity) {
try {
...
insertAuditLog(jsonStr, entityActionLog.getQueryClauseExt());
} catch (Exception ex) {
ex.printStackTrace();
}
}
public int insertAuditLog(String auditContent, String queryRowId) {
String sql = " Insert into LOG (LOG_ID,LOG_DATETIME, LOG_CONTENT) " +
" values (LOG_SEQ.NEXTVAL, SYSDATE, ?) ";
Query query = entityManager.createNativeQuery(sql);
query.setParameter(1, auditContent);
int resultInsert = query.executeUpdate();
return resultInsert;
}
我认为这里的方法调用顺序如下
方法create()
启动一个事务并调用方法secModuleRepository.save(pData)
。
假设 secModuleRepository.save(pData)
有默认的交易传播 REQUIRED
,它参与了以方法 create()
开始的交易。
save()
returns 无一例外地得到 @AfterReturning
建议方法 onSaveExecuted()
的建议。
此事务将在提交事务时导致 DataIntegrityViolationException
。这发生在事务结束时,也就是控件退出方法 create()
.
要记录所有情况下的 save()
方法调用,建议类型应为 @After
或 @Around
并且日志调用不应是事务的一部分提交时回滚。
以下代码使用 TransactionTemplate
启动新事务并插入日志。这个想法是要记录一个新事务,并且给出的示例代码重用了与问题共享的代码,可以根据要求进行改进。
@Aspect
@Component
public class TxnAspect {
@PersistenceContext
private EntityManager entityManager;
// single TransactionTemplate shared amongst all methods in this instance
private final TransactionTemplate transactionTemplate;
// use constructor-injection to supply the PlatformTransactionManager
public TxnAspect(PlatformTransactionManager transactionManager) {
this.transactionTemplate = new TransactionTemplate(transactionManager);
}
@Around(value = "execution(public * save(..)) && this(org.springframework.data.jpa.repository.JpaRepository)")
public Object onSaveExecuted(ProceedingJoinPoint pjp) throws Throwable {
Object responseEntity = null;
try {
responseEntity = pjp.proceed();
} finally {
//gather the details and log
executeInNewTxn("log1","log2");
}
return responseEntity;
}
public Object executeInNewTxn(String str1, String str2) {
transactionTemplate.setPropagationBehavior(Propagation.REQUIRES_NEW.value());
return transactionTemplate.execute(new TransactionCallback<Object>() {
// the code in this method executes in a transactional context
public Object doInTransaction(TransactionStatus status) {
return insertAuditLog(str1, str2);
}
});
}
public int insertAuditLog(String auditContent, String queryRowId) {
String sql = " Insert into LOG (LOG_ID,LOG_DATETIME, LOG_CONTENT) "
+ " values (100, SYSDATE, ?) ";
Query query = entityManager.createNativeQuery(sql);
query.setParameter(1, auditContent);
int resultInsert = query.executeUpdate();
return resultInsert;
}
}
希望对您有所帮助