Неожиданное исключение Rollback в Spring с AOP @AfterReturning - PullRequest
0 голосов
/ 01 мая 2020

Я настроил ведение журнала для каждой операции сохранения, используя 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;
}

1 Ответ

0 голосов
/ 01 мая 2020

Я думаю, что последовательность вызовов методов здесь следующая:

Метод 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;
    }
}

Надеюсь, это поможет

...