Java-事务管理&AOP

本文详细介绍了Spring中的事务管理,包括事务的开启、提交和回滚,以及如何解决业务场景中的数据一致性问题。同时,讨论了AOP(面向切面编程)在事务管理、操作日志记录中的应用,以及如何使用`@Transactional`注解和`Propagation`属性进行定制化控制。
摘要由CSDN通过智能技术生成

事务是一组操作的集合,它是一个不可分割的工作单位,这些操作要么同时成功,要么同时失败

开启事务(一组操作开始前,开启事务):start transaction / begin ;

提交事务(这组操作全部成功后,提交事务):commit ;

回滚事务(中间任何一个操作出现异常,回滚事务):rollback ;

Spring事务管理

例如有一个部门表dept,一个员工表emp,emp中有一个字段dept_id关联dept表,在某一个方法中,要解散部门:删除部门,同时删除该部门下的员工。

存在问题: 当deptMapper.deleteById(id);删除成功,empService.deleteByDeptId(id);想要执行时,发生了异常,这时候即使程序运行抛出了异常,部门也已经被删除了,但是部门下的员工却没有被删除,就会造成数据的不一致。

解决方案:在该方法上开启事务,根据事务的四大特性保证数据的一致性。

注解:@Transactional

位置:业务(service)层的方法上、类上、接口上

作用:将当前方法交给spring进行事务管理,方法执行前,开启事务;成功执行完毕,提交事务;出现异常,回滚事务

@Transactional(rollbackFor = Exception.class) //开启事务 未加rollbackFor:运行时异常回滚事务
@Override
public void deleteById(Integer id) throws Exception {
 
        deptMapper.deleteById(id);
        // int i = 1 / 0;  //人为异常  运行时异常
        // if (true) {
        //     throw new Exception("编译时异常");
        // }
        empService.deleteByDeptId(id);
}

#开启事务管理日志   

logging:
 
level:
   
org.springframework.jdbc.support.JdbcTransactionManager: debug

事务进阶

事务属性-回滚 rollbackFor

默认情况下,只有出现 RuntimeException 才回滚异常。rollbackFor属性用于控制出现何种异常类型,回滚事务。

事务属性-传播行为 propagation

事务传播行为:指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行事务控制。

属性值

含义

REQUIRED

默认值需要事务,有则加入,无则创建新事务

REQUIRES_NEW

需要新事务,无论有无,总是创建新事务

SUPPORTS

支持事务,有则加入,无则在无事务状态中运行

NOT_SUPPORTED

不支持事务,在无事务状态下运行,如果当前存在已有事务,则挂起当前事务

MANDATORY

必须有事务,否则抛异常

NEVER

必须没事务,否则抛异常

REQUIRED :大部分情况下都是用该传播行为即可。

REQUIRES_NEW :当我们不希望事务之间相互影响时,可以使用该传播行为。比如:下订单前需要记录日志,不论订单保存成功与否,都需要保证日志记录能够记录成功。

例如:在上述问题中,需要实现以下功能:解散部门时,无论成功失败,记录操作日志

@Transactional(rollbackFor = Exception.class) //开启事务 未加rollbackFor:运行时异常回滚事务
@Override
public void deleteById(Integer id) throws Exception {
    // try {
        deptMapper.deleteById(id);
        // int i = 1 / 0;
        // if (true) {
        //     throw new Exception("编译时异常");
        // }
        empService.deleteByDeptId(id);
      } finally {
          DeptLog log = new DeptLog();
          log.setCreateTime(LocalDateTime.now());
          log.setDescription("删除部门id:" + id + ",并删除部门下的员工");
          deptLogService.insert(log);
    
     }

}

@Transactional(propagation = Propagation.REQUIRES_NEW)
@Override
public void insert(DeptLog deptLog) {
    deptLogMapper.insert(deptLog);
}

AOP

面向切面编程,其实就是面向特定方法编程

场景

案例部分功能运行较慢,定位执行耗时较长的业务方法,此时需要统计每一个业务方法的执行耗时。

实现:

动态代理是面向切面编程最主流的实现。而SpringAOP是Spring框架的高级技术,旨在管理bean对象的过程中,主要通过底层的动态代理机制,对特定的方法进行编程。

导入依赖

<dependency>
    <
groupId>org.springframework.boot</groupId>
    <
artifactId>spring-boot-starter-aop</artifactId>
</
dependency>

场景使用

记录操作日志、权限控制、事务管理

优势:

代码无侵入、减少重复代码、提高开发效率、维护方便

@Autowired
private OperateLogMapper operateLogMapper;

@Autowired
private HttpServletRequest request;


    /*
     0. 创建数据表,创建对应的实体类以及 mapper【我给你提供】
    1. 先创建一个切面类【@Component @Aspect】
    2. 定义一个方法,方法上面通知类型为@Around(注解的方式去匹配对应的方法)
    //     2.1 先创建一个注解
    //     2.2 在对应的方法【实现类的方法】上面添加注解
    */
@Around("pt()")
public Object around(ProceedingJoinPoint pj) throws Throwable {
    log.info("从此处开始AOP方法");

    // 3. 方法中
    //      3.1 我们需要获取当前操作人【从request中获取token,然后解析token ,把解析结果强转为map】
   
    // String token = (String) args[args.length -1];

    String token = request.getHeader("token");

    Map<String, Object> map = (Map<String, Object>) JWTUtils.parseJWTStr(token);
    Integer operateUser = (Integer) map.get("id");
    // Integer operateUser = (Integer) args[args.length -1];
 Object[] args = pj.getArgs();

    //      3.2 操作时间: 当前的时间
    LocalDateTime operateTime = LocalDateTime.now();

    //      3.3 获取方法的相关信息
    String className = pj.getTarget().getClass().getName();
    String methodName = pj.getSignature().getName();

    //      3.4 定义一个开始的时间
    long methodBeginTime = System.currentTimeMillis();

    //      3.5 调用连接点的方法
    Object result = pj.proceed();

    //      3.6 定义一个结束时间,统计时长
    long methodEndTime = System.currentTimeMillis();

    //      3.6 把这些信息保存到对象中,然后再插入到数据库
    OperateLog operateLog = new OperateLog(null,operateUser,operateTime, className, methodName, Arrays.toString(args), (String)result, (methodEndTime - methodBeginTime));
    operateLogMapper.insert(operateLog);
    log.info("AOP方法到此结束");
    //      3.7 返回连接点执行完方法之后的结果
    return result;

}

AOP核心概念 

AOP进阶

执行顺序

@Around -> @Before->@AfterReturning/@AfterThrowing->@After->@Around

 也可以使用@Order(数字值)来自定义执行顺序

切入点表达式-execution

execution 主要根据方法的返回值、包名、类名、方法名、方法参数等信息来匹配,语法为:

execution(访问修饰符?  返回值  包名.类名.?方法名(方法参数) throws 异常?)

其中带 ? 的表示可以省略的部分

访问修饰符:可省略(比如: public、protected)

包名.类名: 可省略

throws 异常:可省略(注意是方法上声明抛出的异常,不是实际抛出的异常)

可以使用通配符描述切入点

* :单个独立的任意符号,可以通配任意返回值、包名、类名、方法名、任意类型的一个参数,也可以通配包、类、方法名的一部分

execution(* com.*.service.*.update*(*))

.. :多个连续的任意符号,可以通配任意层级的包,或任意类型、任意个数的参数

execution(* com.it..DeptService.*(..))

注意:

根据业务需要,可以使用 且(&&)、或(||)、非(!) 来组合比较复杂的切入点表达式。

书写建议

所有业务方法名命名时尽量规范,方便切入点表达式快速匹配。如:查询类方法都是 find 开头,更新类方法都是 update开头。

描述切入点方法通常基于接口描述,而不是直接描述实现类,增强拓展性

在满足业务需要的前提下,尽量缩小切入点的匹配范围。如:包名匹配尽量不使用 ..,使用 * 匹配单个包。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值