【问题标题】:UnexpectedRollbackException in Spring with AOP @AfterReturning带有 AOP @AfterReturning 的 Spring 中的 UnexpectedRollbackException
【发布时间】:2020-05-01 06:56:04
【问题描述】:

我使用 Spring AOP 为每个保存操作设置了一个日志记录,其中我用@Transactional 进行了标记。问题是当我的保存方法抛出异常并仅用回滚标记事务时,但使用 AOP 的日志记录操作不知道。这就是为什么它导致:

'事务静默回滚,因为它已被标记为仅回滚'。

如何克服这种情况?

我的保存方法:

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

【问题讨论】:

  • 您是否尝试添加带有@AfterThrowing 注释的方法以从保存方法中捕获异常
  • 它无法捕获异常,因为 save 方法在遇到事务方法(创建方法)的最后一条语句之前不会抛出任何异常。这就是为什么它会首先跳转到我的日志记录方法并使 UnexpectedRollbackException 发生。

标签: java spring-boot spring-data-jpa spring-aop


【解决方案1】:

我认为这里的方法调用顺序如下

方法create()启动事务并调用方法secModuleRepository.save(pData)

假设secModuleRepository.save(pData) 具有默认的事务传播REQUIRED,它参与了使用方法create() 启动的事务。

save() 无异常返回,并通过@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;
    }
}

希望对你有帮助

【讨论】:

  • 我尝试使用 @Transactional(Propagation = Propagation.REQUIRES_NEW) 创建一个新事务,但它仍然使用相同的事务回退。但是切换到 TransactionTemplate,它现在就像一个魅力。非常感谢。
  • 如果您的问题得到解答,如果您觉得这很有用,请点赞(向上箭头)并将其标记为答案(勾号)?
  • 我已经为你的回答投了赞成票,但我的声誉似乎低于 15,所以它被记录但没有显示为得分。感谢您的回答,我发现它非常有用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多