事务+切面踩坑~~

一,背景描述

对于方法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);
    }
}
相关推荐
码事漫谈16 分钟前
手把手带你部署本地模型,让你Token自由(小白专属)
前端·后端
码农BookSea1 小时前
ReAct:让大模型学会边想边做
后端·ai编程
码农BookSea1 小时前
10分钟掌握 JSON-RPC 协议,面试加分、设计不踩坑
后端
凤年徐2 小时前
C++手撕红黑树:从0到200行,拿下STL map底层核心
c++·后端·算法
IT_陈寒2 小时前
Python的列表推导式里藏了个坑,差点让我加班到凌晨
前端·人工智能·后端
卷无止境3 小时前
podman与docker的区别和生产环境最佳实践
后端
程途知微3 小时前
ConcurrentHashMap线程安全实现原理全解析
java·后端
Mars酱3 小时前
1分钟编写贪吃蛇 | JSnake贪吃蛇单机版
java·后端·开源
卷卷说风控3 小时前
养了10年风控,今年开始养「虾」了
后端
王码码20353 小时前
Go语言中的Elasticsearch操作:olivere实战
后端·golang·go·接口