事务管理与 AOP

Spring 学习第六天:事务管理与 AOP

在后端开发中,保证数据的一致性至关重要。然而,在实际开发中总会有各种异常情况发生,导致数据可能出现问题。本文将通过一个示例讲解如何使用 事务管理AOP 解决这些问题。


事务管理

场景描述

假设我们有两张表:

  1. dept:存储部门信息
ID部门名称创建时间更新时间
1销售部2024-11-06 13:36:452024-11-11 16:56:02
2教研部2024-11-06 13:36:452024-11-06 13:36:45
3咨询部2024-11-06 13:36:452024-11-06 13:36:45
4就业部2024-11-06 13:36:452024-11-06 13:36:45
5人事部2024-11-06 13:36:452024-11-06 13:36:45
7司令部2024-11-11 15:28:312024-11-11 15:28:31
9学工部2024-11-11 16:56:432024-11-11 16:56:43

2.emp:存储员工信息

员工ID姓名性别年龄职位部门ID
101张三28销售经理1
102李四25销售专员1
103王五30教研主任2
104赵六26教学助理2
105钱七35高级咨询师3
106孙八24咨询顾问3
107周九32就业指导员4
108吴十27人事专员5
109郑十一40人事主管5
110王十二38部门司令7
111刘十三29学工辅导员9
112陈十四26学工助理9

我们需要实现 删除某个部门 的操作,步骤如下:

  1. 删除 dept 表中对应部门的记录。
  2. 删除 emp 表中该部门的所有员工。
public void DeleteById(Integer id) {  
    //删除部门  
    deptMapper.delete(id);  
    //删除员工Id  
    empMapper.deleteByDeptId(id);  
}

通过调用对于的mapper执行删除操作,这切看起来都非常合理,但是这里面存在一个很大的问题是,如果deptMapper.delete(id);顺利执行,但是empMapper.deleteByDeptId(id);中出现了某种异常,导致执行失败,程序会抛出异常,导致员工无法正常删除,员工的脏数据就会一直存在数据库中。

解决方案:事务管理

Spring 提供了事务管理的支持,该注解会为标注的方法或类提供事务支持,确保这些方法中的数据库操作要么全部成功提交(commit),要么在出错时全部回滚(rollback),在此注解中,rollbackFor = Exception.class 表示所有异常(包括运行时异常和受检异常)都会触发事务回滚。也就是说即使发生异常,我们可以返回错误信息,撤销已经完成的删除操作,直到异常解决。

通过 @Transactional 注解,可以实现以下效果:

  1. 所有操作要么全部成功提交(commit
  2. 要么在发生异常时全部回滚(rollback
@Transactional(rollbackFor = Exception.class)  
public void DeleteById(Integer id) {  
    // 删除部门  
    deptMapper.delete(id);  
    // 删除员工  
    empMapper.deleteByDeptId(id);  
}
  • @Transactional 注解:声明该方法需要事务支持。
  • rollbackFor = Exception.class:指明发生任何异常时,回滚所有操作。

这样,若 empMapper.deleteByDeptId(id) 抛出异常,整个事务将回滚,deptMapper.delete(id) 的删除操作也会撤销,从而保证数据一致性。

AOP:面向切面编程

什么是 AOP?

AOP(Aspect Oriented Programming) 是一种编程思想,用于动态地将某些通用功能(如日志记录、性能监控、事务管理等)附加到目标方法中,而无需修改目标方法的代码。它由以下几个部分组成

  • 连接点(JoinPoint)
    可以被 AOP 控制的方法(暗含方法执行时的相关信息)
  • 通知(Advice)
    指定义的重复逻辑,也就是共性功能(最终体现为一个方法)
  • 切入点(PointCut)
    匹配连接点的条件,通知仅会在符合切入点的条件时应用 )
  • 切面(Aspect)
    描述通知与切入点的对应关系(通知 + 切入点)
  • 目标对象(Target)
    通知所应用的对象

我一向不爱听繁琐的概念,上面的AOP的定义对于入门实在太不友好了,下面我们举一个小例子来解释AOP是什么,能干什么。
假定我们要给项目中的每一个实现方法都计算它的运行时间,最简单暴力的当然是在每一个方法的开头记录一个时间,在末尾也记录一个时间,两个时间的插值就得到了方法的运行时间。

示例:方法耗时统计

常规实现(不使用 AOP)

如果我们想统计项目中所有方法的运行时间,传统做法是直接在每个方法内添加以下代码:

long begin = System.currentTimeMillis();  
// 在这中间写执行的方法 
//方法。。。。。
long end = System.currentTimeMillis();  
long costTime = end - begin;

这段代码要加到每一个方法前面,实在太繁琐了。这时候就到AOP大显身手了。简单来说,AOP支持我们将任何方法夹杂到一串我们自定义的代码中(可以前中后任何地方),所以我们只需要通过AOP加到上面那串计算时间的代码中即可。通过 AOP,我们可以将 运行时间统计的逻辑 单独抽取出来,并自动应用到指定的方法上。

实现步骤
  1. 定义一个切面类
@Component  
@Aspect  
@Slf4j  
public class TimeAspect {  
    @Around("execution(* com.itheima.practice.Service.*.*(..))") // 切入点表达式  
    public Object recordLog(ProceedingJoinPoint joinPoint) throws Throwable {  
        // 记录开始时间  
        long begin = System.currentTimeMillis();  
  
        // 执行原方法  
        Object result = joinPoint.proceed();  
  
        // 记录结束时间  
        long end = System.currentTimeMillis();  
  
        // 计算耗时  
        long costTime = end - begin;  
        log.info("方法耗时: {} ms", costTime);  
  
        return result;  
    }  
}

声明的方式与普通类的定义方式没什么区别,不同之处在于在类上要声明@Aspect注解,表明这是一个拦截器,
代码中

  • @Around:声明拦截类型,表示在目标方法前后执行代码。
  • execution 表达式:定义拦截的目标方法范围,例如 com.itheima.practice.Service 包下的所有方法。
  • joinPoint.proceed():调用原始目标方法。
  1. 效果
    AOP 自动为指定范围内的方法插入耗时统计代码,无需手动在每个方法中重复写逻辑。
    代表你要拦截哪些方法,这里我要拦截的是我的目录中com.itheima.practice.Service的所有方法,切入点表达式中写你的方法所在的相对路径即可,关于切入点表达式这里不细讲。

切入点与通知类型

Spring AOP 提供多种通知类型,根据不同的需求可以选择合适的拦截时机。

  • @Before
    在目标方法执行前运行通知逻辑。

  • @After
    在目标方法执行后运行通知逻辑,无论成功或异常都会执行。

  • @AfterReturning
    在目标方法成功执行后运行通知逻辑(不会拦截异常)。

  • @AfterThrowing
    在目标方法抛出异常后运行通知逻辑。

  • @Around
    环绕目标方法执行,在目标方法的前后都可以插入逻辑(包含成功和异常场景)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值