事务管理
事务回顾
Spring事务管理
案例:解散部门
@Transactional
例如:
(源代码)
service包
DeptServiceImpl
java
package com.itheima.service.impl;
import com.itheima.mapper.DeptMapper;
import com.itheima.pojo.Dept;
import com.itheima.service.DeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.List;
@Service
public class DeptServiceImpl implements DeptService {
@Autowired
private DeptMapper deptMapper;
//根据id删除部门
@Transactional //spring事务管理
@Override
public void delete(Integer id) {
deptMapper.deleteById(id);//根据id删除部门
deptMapper.deleteByDeptId(id);//根据id删除部门下的员工数据
}
}
mapper包
DeptMapper
java
package com.itheima.mapper;
import com.itheima.pojo.Dept;
import org.apache.ibatis.annotations.*;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import java.util.List;
/**
* 部门管理
*/
@Mapper
public interface DeptMapper {
//根据id删除部门
@Delete("delete from dept where id = #{id}")
void deleteById(Integer id);
//根据ID删除部门下的员工数据
@Delete("delete from emp where dept_id=#{deptId}")
void deleteByDeptId(Integer deptId);
}
spring事务管理日志
application.yml
#spring事务管理日志
logging:
level:
org.springframework.jdbc.support.JdbcTransactionManager: debug
事务进阶
事务属性-回滚

例如:
java
//根据id删除部门
@Transactional(rollbackFor = Exception.class) //spring事务管理
@Override
public void delete(Integer id) {
deptMapper.deleteById(id);//根据id删除部门
deptMapper.deleteByDeptId(id);//根据id删除部门下的员工数据
}
事务属性-传播行为
什么是传播行为?

常见的事务传播行为

案例:解散部门时,记录操作日志

例如:
(源代码)
service.impl包
DeptServiceImpl
java
package com.itheima.service.impl;
import com.itheima.mapper.DeptMapper;
import com.itheima.pojo.Dept;
import com.itheima.pojo.DeptLog;
import com.itheima.service.DeptLogService;
import com.itheima.service.DeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.List;
@Service
public class DeptServiceImpl implements DeptService {
@Autowired
private DeptMapper deptMapper;
@Autowired
private DeptLogService deptLogService;
//根据id删除部门
@Transactional(rollbackFor = Exception.class) //spring事务管理
@Override
public void delete(Integer id) {
try {
deptMapper.deleteById(id);//根据id删除部门
deptMapper.deleteByDeptId(id);//根据id删除部门下的员工数据
} finally {
DeptLog deptLog = new DeptLog();
deptLog.setCreateTime(LocalDateTime.now());
deptLog.setDescription("执行了解散部门的操作,此次解散的部门是"+ id + "号部门");
deptLogService.insert(deptLog);
}
}
}
DeptLogServiceImpl
java
package com.itheima.service.impl;
import com.itheima.mapper.DeptLogMapper;
import com.itheima.pojo.DeptLog;
import com.itheima.service.DeptLogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service
public class DeptLogServiceImpl implements DeptLogService {
@Autowired
private DeptLogMapper deptLogMapper;
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Override
public void insert(DeptLog deptLog) {
deptLogMapper.insert(deptLog);
}
}
service包
DeptLogService
java
package com.itheima.service;
import com.itheima.pojo.DeptLog;
public interface DeptLogService {
void insert(DeptLog deptLog);
}
mapper包
** **DeptLogMapper** **
java
package com.itheima.mapper;
import com.itheima.pojo.DeptLog;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface DeptLogMapper {
@Insert("insert into dept_log(create_time,description) values(#{createTime},#{description})")
void insert(DeptLog log);
}
pojo包
DeptLog
java
package com.itheima.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class DeptLog {
private Integer id;
private LocalDateTime createTime;
private String description;
}
表结构
java
create table dept_log(
id int auto_increment comment '主键ID' primary key,
create_time datetime null comment '操作时间',
description varchar(300) null comment '操作描述'
)comment '部门操作日志表';
AOP基础
AOP概述

AOP快速入门

例如:
导入依赖
java
<!--AOP-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
编写AOP程序
java
package com.itheima.aop;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Component
@Aspect //AOP类
@Slf4j
public class TimeAspect {
@Around("execution(* com.itheima.service.*.*(..))")
public Object recordTime(ProceedingJoinPoint joinPoint) throws Throwable {
//1.记录开始时间
long begin = System.currentTimeMillis();
//2.调用原始方法运行
Object result = joinPoint.proceed();
//3.记录结束时间,计算方法执行耗时
long end = System.currentTimeMillis();
log.info(joinPoint.getSignature() + "方法执行耗时: {} ms", end - begin);
return result;
}
}
AOP应用场景和优势

核心概念

AOP执行流程

AOP进阶
通知类型

例如:
(源代码)
java
package com.itheima.aop;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component
@Aspect
@Slf4j
public class MyAspect {
@Pointcut("execution(* com.itheima.service.impl.DeptServiceImpl.*(..))")//切入点
public void pt(){}
//环绕通知
@Around("pt()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("around before");
Object result = joinPoint.proceed();
System.out.println("around after");
return result;
}
//前置通知
@Before("pt()")
public void before() {
System.out.println("before");
}
//后置通知
@After("pt()")
public void after() {
System.out.println("after");
}
//返回后通知
@AfterReturning("pt()")
public void afterReturning() {
System.out.println("afterReturning");
}
//异常后通知
@AfterThrowing("pt()")
public void afterThrowing() {
System.out.println("afterThrowing");
}
}
(运行结果截图)

@PointCut

通知顺序

切入点表达式
什么是切入点表达式?

execution


例如:
java
package com.itheima.aop;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component
@Aspect
@Slf4j
public class MyAspect7 {
//匹配DeptServiceImpl中的 list() 和 delete(Integer id) 方法
@Pointcut("execution(* com.itheima.service.DeptService.list()) || execution(* com.itheima.service.DeptService.delete(java.lang.Integer))")
private void pt(){}
@Before("pt()")
public void before() {
System.out.println("MyAspect7 ...before...");
}
}
书写建议

@annotation

例如:
java
package com.itheima.aop;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component
@Aspect
@Slf4j
public class MyAspect7 {
//匹配DeptServiceImpl中的 list() 和 delete(Integer id) 方法
//@Pointcut("execution(* com.itheima.service.DeptService.list()) || execution(* com.itheima.service.DeptService.delete(java.lang.Integer))")
@Pointcut("@annotation(com.itheima.aop.MyLog)")
private void pt(){}
@Before("pt()")
public void before() {
System.out.println("MyAspect7 ...before...");
}
}
连接点

环绕通知

前置通知

例如:
java
package com.itheima.aop;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Component
@Aspect
@Slf4j
public class MyAspect8 {
@Pointcut("execution(* com.itheima.service.DeptService.*(..))")
private void pt(){}
@Before("pt()")
public void before(JoinPoint joinPoint){
System.out.println("MyAspect8 ...before...");
}
@Around("pt()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("MyAspect8 ...around before...");
//1.获取 目标对象的类名
String name = joinPoint.getTarget().getClass().getName();
log.info("目标对象的类名:{}",name);
//2.获取 目标对象的方法名
String methodName = joinPoint.getSignature().getName();
log.info("目标对象的方法名:{}",methodName);
//3.获取 目标方法运行时传入的参数
Object[] args = joinPoint.getArgs();
log.info("目标方法运行时传入的参数:{}", Arrays.toString(args));
//4.放行 目标方法执行
Object result = joinPoint.proceed();
//5.获取 目标方法运行的返回值
log.info("目标方法运行的返回值:{}",result);
System.out.println("MyAspect8 ...around after...");
return result;
}
}
AOP案例:记录操作日志

实现步骤

代码实现
anno包
Log
java
package com.itheima.anno;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)//注解什么时候生效
@Target(ElementType.METHOD)//当前注解可以作用在什么地方
public @interface Log {
}
aop包
LogAspect
java
package com.itheima.aop;
import com.alibaba.fastjson.JSONObject;
import com.itheima.mapper.OperateLogMapper;
import com.itheima.pojo.OperateLog;
import com.itheima.utils.JwtUtils;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.Arrays;
@Component
@Aspect@Slf4j
public class LogAspect {
@Autowired
private HttpServletRequest request;
@Autowired
private OperateLogMapper operateLogMapper;
@Around("@annotation(com.itheima.anno.Log)")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
//操作人ID -- 当前登录员工ID
//获取请求头中的jwt令牌,然后解析jwt令牌
String jwt = request.getHeader("token");
Claims claims = JwtUtils.parseJWT(jwt);
Integer operatorUser = (Integer) claims.get("id");
//操作时间
LocalDateTime operateTime = LocalDateTime.now();
//操作类名
String className = joinPoint.getTarget().getClass().getName();
//操作方法名
String methodName = joinPoint.getSignature().getName();
//操作方法参数
Object[] args = joinPoint.getArgs();
String methodParams = Arrays.toString(args);
long begin = System.currentTimeMillis();
//调用原始目标方法运行
Object result = joinPoint.proceed();
long end = System.currentTimeMillis();
//方法返回值
String returnValue = JSONObject.toJSONString(result);
//操作耗时
Long costTime = end - begin;
//记录操作日志
OperateLog operateLog = new OperateLog(null,operatorUser,operateTime,className,methodName,methodParams,returnValue,costTime);
operateLogMapper.insert(operateLog);
log.info("AOP记录操作日志:{}",operateLog);
return result;
}
}
controller包
DeptController
java
package com.itheima.controller;
import com.itheima.anno.Log;
import com.itheima.pojo.Dept;
import com.itheima.pojo.Result;
import com.itheima.service.DeptService;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.annotations.Delete;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 部门管理Controller
*/
@Slf4j
@RestController
public class DeptController {
@Autowired
private DeptService deptService;
//查询部门
@GetMapping("/depts")
public Result list(){
log.info("查询全部部门数据");
List<Dept> deptList = deptService.list();
return Result.success(deptList);
}
//根据id删除部门
@Log
@DeleteMapping("/depts/{id}")
public Result deleteById(@PathVariable Integer id){
log.info("根据id删除部门"+id);
deptService.delete(id);
return Result.success();
}
//新增部门
@Log
@PostMapping("/depts")
public Result add(@RequestBody Dept dept){
log.info("新增部门"+dept);
deptService.add(dept);
return Result.success();
}
//修改部门
@Log
@GetMapping("/depts/{id}")
public Result getByID(@PathVariable Integer id) {
log.info("获取部门ID:{}",id);
Dept dept = deptService.getByID(id);
return Result.success(dept);
}
@PutMapping("/depts")
public Result update(@RequestBody Dept dept) {
log.info("修改部门:{}",dept);
deptService.update(dept);
return Result.success();
}
}
EmpController
java
package com.itheima.controller;
import com.itheima.anno.Log;
import com.itheima.mapper.EmpMapper;
import com.itheima.pojo.Emp;
import com.itheima.pojo.PageBean;
import com.itheima.pojo.Result;
import com.itheima.service.DeptService;
import com.itheima.service.EmpService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDate;
import java.util.List;
/**
* 员工管理Controller
*/
@Slf4j
@RestController
@RequestMapping("/emps")
public class EmpController {
@Autowired
private EmpService empService;
@GetMapping
public Result page(@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer pageSize,
String name, short gender,
@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,
@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end
) {
log.info("分页查询,参数:{},{},{},{},{},{}",page,pageSize,name,gender,begin,end);
//调用service分页查询
PageBean pageBean = empService.page(page,pageSize,name,gender,begin,end);
return Result.success(pageBean);
}
//根据id删除员工
@Log
@DeleteMapping("/{ids}")
public Result delete(@PathVariable List<Integer> ids) {
log.info("批量删除员工,ids:{}",ids);
empService.delete(ids);
return Result.success();
}
//增加员工
@Log
@PostMapping
public Result save(@RequestBody Emp emp) {
log.info("新增员工:{}",emp);
empService.save(emp);
return Result.success();
}
//根据id查询员工
@GetMapping("/{id}")
public Result getById(@PathVariable Integer id) {
log.info("根据id查询员工{}",id);
Emp emp = empService.getById(id);
return Result.success(emp);
}
//修改员工
@Log
@PutMapping
public Result update(@RequestBody Emp emp) {
log.info("更新员工信息:{}",emp);
empService.update(emp);
return Result.success();
}
}
service包
DeptLogService
java
package com.itheima.service;
import com.itheima.pojo.DeptLog;
public interface DeptLogService {
void insert(DeptLog deptLog);
}
DeptLogServiceImpl
java
package com.itheima.service.impl;
import com.itheima.mapper.DeptLogMapper;
import com.itheima.pojo.DeptLog;
import com.itheima.service.DeptLogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service
public class DeptLogServiceImpl implements DeptLogService {
@Autowired
private DeptLogMapper deptLogMapper;
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Override
public void insert(DeptLog deptLog) {
deptLogMapper.insert(deptLog);
}
}
mapper包
OperateLogMapper
java
package com.itheima.mapper;
import com.itheima.pojo.OperateLog;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface OperateLogMapper {
//插入日志数据
@Insert("insert into operate_log (operate_user, operate_time, class_name, method_name, method_params, return_value, cost_time) " +
"values (#{operateUser}, #{operateTime}, #{className}, #{methodName}, #{methodParams}, #{returnValue}, #{costTime});")
public void insert(OperateLog log);
}
pojo包
OperateLog
java
package com.itheima.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class OperateLog {
private Integer id; //ID
private Integer operateUser; //操作人ID
private LocalDateTime operateTime; //操作时间
private String className; //操作类名
private String methodName; //操作方法名
private String methodParams; //操作方法参数
private String returnValue; //操作方法返回值
private Long costTime; //操作耗时
}
(运行结果截图)




