事务+切面踩坑~~

一,背景描述

对于方法A,做一个切面,前后记录日志,如果这个方法A事务包裹,此时会发生什么,且看代码

二,上代码

1,定义一个切面

less 复制代码
@Component
@Aspect
@Slf4j
public class logAspect {

    @Resource
    LogService logService;

    @Pointcut("execution(public * com.service.ShopResultService.syn*(..))")
    public void logPointCut() {
    }

    @Around("logPointCut()")
    public void process(ProceedingJoinPoint joinPoint) {
        String name = joinPoint.getSignature().getName();
        LogRecord record = logService.start(name);
        try {
            joinPoint.proceed();
            logService.success(record);
        } catch (Throwable throwable) {
            logService.fail(record);
        }
    }
}

定义一个日志切面,里面有logPointCut切点,对于切点,在方法前后打日志,LogRecord对应mysql表

2,日志服务

csharp 复制代码
@Service
public class LogService {

    @Autowired
    LogRecordMapper logRecordhMapper;

    /**
     * 新增
     */
    public LogRecord start(String taskDef) {
        //新增日志
        LogRecord record = new LogRecord();
        record.setProjectNo(taskDef);
        record.setStatus("doing");
        logRecordhMapper.insert(record);
        return record;
    }

    /**
     * 成功
     */
    public void success(LogRecord record) {
        //更新日志
        auth.setStatus("success");
        logRecordhMapper.updateById(record);
    }

    /**
     * 失败
     */
    public void fail(LogRecord record) {
        //失败日志
        auth.setStatus("fail");
        logRecordhMapper.updateById(record);
    }
}

如果任务执行成功,日志更新为success,否则是fail。

3,被切的服务

csharp 复制代码
@Transactional
public void synData() {
    UserData userData = new UserData();
    userData.setShopName("test");
    UserDataMapper.insert(userData);
    System.out.println("over");
}

4,问题

arduino 复制代码
 public LogRecord start(String taskDef)

发现日志方法虽然没有加事务,但其实是在事务里的,也就是说日志方法记录完成后,表暂态是没有记录的,此时日志是被切面事务包裹在一起,也就是说,如果synData失败,切面也会抛异常,因为update一条数据库不存在的记录。

三,结论

spring事务和切面都是通过代理完成,代理链的设计导致这两个逻辑绑定在一起

解决方案:1,使用注解,切面在事务之前执行

java 复制代码
@Order(Ordered.LOWEST_PRECEDENCE - 1)
less 复制代码
@Component
@Aspect
@Slf4j
@Order(Ordered.LOWEST_PRECEDENCE - 1)
public class logAspect {

    @Resource
    LogService logService;

    @Pointcut("execution(public * com.service.ShopResultService.syn*(..))")
    public void logPointCut() {
    }

    @Around("logPointCut()")
    public void process(ProceedingJoinPoint joinPoint) {
        String name = joinPoint.getSignature().getName();
        LogRecord record = logService.start(name);
        try {
            joinPoint.proceed();
            logService.success(record);
        } catch (Throwable throwable) {
            logService.fail(record);
        }
    }
}

解决方案2,使用spring事务隔离级别,将两个事务隔离开

csharp 复制代码
@Service
public class LogService {

    @Autowired
    LogRecordMapper logRecordhMapper;

    /**
     * 新增
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public LogRecord start(String taskDef) {
        //新增日志
        LogRecord record = new LogRecord();
        record.setProjectNo(taskDef);
        record.setStatus("doing");
        logRecordhMapper.insert(record);
        return record;
    }

    /**
     * 成功
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void success(LogRecord record) {
        //更新日志
        auth.setStatus("success");
        logRecordhMapper.updateById(record);
    }

    /**
     * 失败
     */     
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void fail(LogRecord record) {
        //失败日志
        auth.setStatus("fail");
        logRecordhMapper.updateById(record);
    }
}
相关推荐
风象南5 小时前
很多人说,AI 让技术平权了,小白也能乱杀老师傅 ?
人工智能·后端
雨中飘荡的记忆6 小时前
ElasticJob分布式调度从入门到实战
java·后端
Se7en2587 小时前
推理平台全景
后端
大漠_w3cpluscom7 小时前
你学不会 CSS,不是笨,是方向错了
后端
cipher10 小时前
ERC-4626 通胀攻击:DeFi 金库的"捐款陷阱"
前端·后端·安全
毅航11 小时前
自然语言处理发展史:从规则、统计到深度学习
人工智能·后端
JxWang0511 小时前
Task04:字符串
后端
树獭叔叔11 小时前
10-让模型更小更聪明,学而不忘:知识蒸馏与持续学习
后端·aigc·openai
JxWang0512 小时前
Task02:链表
后端