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;
    }
}

希望对您有所帮助