事务+切面踩坑~~

一,背景描述

对于方法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);
    }
}
相关推荐
武藤一雄6 小时前
C# 关于多线程如何实现需要注意的问题(持续更新)
windows·后端·microsoft·c#·.net·.netcore·死锁
程序新视界7 小时前
为什么不建议基于Multi-Agent来构建Agent工程?
人工智能·后端·agent
Victor3567 小时前
Hibernate(29)什么是Hibernate的连接池?
后端
Victor3567 小时前
Hibernate(30)Hibernate的Named Query是什么?
后端
源代码•宸8 小时前
GoLang八股(Go语言基础)
开发语言·后端·golang·map·defer·recover·panic
czlczl200209258 小时前
OAuth 2.0 解析:后端开发者视角的原理与流程讲解
java·spring boot·后端
颜淡慕潇8 小时前
Spring Boot 3.3.x、3.4.x、3.5.x 深度对比与演进分析
java·后端·架构
布列瑟农的星空8 小时前
WebAssembly入门(一)——Emscripten
前端·后端
小突突突9 小时前
Spring框架中的单例bean是线程安全的吗?
java·后端·spring
iso少年9 小时前
Go 语言并发编程核心与用法
开发语言·后端·golang