You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在@Around切面中实现事务提交并创建数据库日志?

问题分析与解决方案

核心问题根源

你的日志无法提交的原因是:切面和目标方法的事务上下文绑定在一起,当目标方法抛出异常触发回滚时,切面中所有未提交的数据库操作(包括日志插入)都会被一起回滚。即使给目标方法设置REQUIRES_NEW或NESTED,只要切面本身的事务和主事务关联,日志操作依然会被主事务的回滚影响。

解决方案:让日志操作在独立事务中执行

要确保日志不受主事务回滚影响,需要让日志的写入操作在独立的事务中完成,执行后立即提交,与主事务完全隔离。具体步骤如下:

1. 移除切面方法上的@Transactional

切面不需要参与主事务,它只负责协调日志记录和目标方法的执行,所以直接去掉切面方法上的事务注解。

2. 给日志服务方法设置REQUIRES_NEW传播级别

修改日志服务的方法,强制每次调用都开启新事务,执行完成后立即提交,不受主事务状态影响:

@Service
public class RegistrationRequestLogService {
    // 每次调用都开启新事务,执行后立即提交
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public RegistrationRequestLog logRegistrationRequest(RegisterRequest request) {
        RegistrationRequestLog log = new RegistrationRequestLog();
        // 设置请求日志属性(如请求参数、时间戳等)
        logRepository.save(log);
        return log;
    }
}

@Service
public class RegistrationResponseLogService {
    // 每次调用都开启新事务,执行后立即提交
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void logRegistrationResponse(RegistrationRequestLog requestLog, String message) {
        RegistrationResponseLog responseLog = new RegistrationResponseLog();
        responseLog.setRequestLogId(requestLog.getId());
        responseLog.setMessage(message);
        responseLogRepository.save(responseLog);
    }
}

3. 修改切面代码(移除事务注解,保持原有逻辑)

@Around(value = "execution(* com.service.RegistrationService.register(..)) && args(registerRequest)")
public Object registrationLogAspect(ProceedingJoinPoint proceedingJoinPoint, RegisterRequest registerRequest) throws Throwable {
    RegistrationRequestLog registrationRequestLog = null;
    try {
        // 请求日志已在独立事务中提交
        registrationRequestLog = registrationRequestLogService.logRegistrationRequest(registerRequest);
        
        // 执行目标方法,主事务在此运行
        String result = (String) proceedingJoinPoint.proceed();     
        
        // 响应日志在独立事务中提交
        registrationResponseLogService.logRegistrationResponse(registrationRequestLog, result);
        return result;
    } catch (Exception e) {
        if (registrationRequestLog != null) {
            // 异常日志在独立事务中提交,不受主事务回滚影响
            registrationResponseLogService.logRegistrationResponse(registrationRequestLog, "异常信息:" + e.getMessage());
        }
        // 重新抛出异常,触发主事务回滚
        throw e;
    }
}

原理说明

  • REQUIRES_NEW传播级别会强制开启一个全新的事务,与当前主事务完全隔离,当日志方法执行完成后,新事务立即提交,即使后续主事务回滚,已提交的日志也不会被撤销。
  • 切面不再持有事务上下文,仅作为执行流程的拦截器,协调日志记录和目标方法的执行逻辑。
  • 目标方法的@Transactional保持原有配置即可,异常抛出后会正常触发主事务回滚,同时日志已被独立事务持久化。

内容的提问来源于stack exchange,提问作者Omar Abdelhady

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 19:22:53