spring事务管理+AOP
项目文件在之前的公司管理系统上增添的….
—事务管理—
事务是一组操作的集合,它是一个不可分割的工作单位,这些操作要么同时成功,要么同时失败。
简单案例:删除部门,同时删除该部门下的员工。
直接在删除部门方法的Service类下添加个删除员工的方法并在EmpMapper里添加删除对应部门员工的方法。
如果想在让处理的事件回滚并报错,比如在DeptService里删除方法里,添加上@Transactional(rollbackFor = Exception.class),这里的rollbackFor指出现什么异常就回滚,这里是所有异常。为了测试就直接在方法里面的删除部门的deptMapper方法下编写:
if (true){
throw new Exception("出差啦..");
}
这时候再去删除部门就会报错,并且部门也删除不了
事务传播行为:比如要记录比较重要的操作时,如删除部门,无论成功还是失败都要记录日志。这时就再创建一个数据库用于记录日志。在DeptService删除方法下调用添加日志的方法,然后在DeptLogService类下的添加的日志方法下required:大部分情况下都是用该传播行为即可;requires_new:当不希望事务之间相互影响
DeptLogServiceImpl类里面:
@Service
public class DeptLogServiceImpl implements DeptLogService {
@Autowired
private DeptLogMapper deptLogMapper;
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Override
public void insertLog(DeptLog deptLog) {
deptLogMapper.insert(deptLog);
}
}
—AOP—
Aspect Oriented Programming,面向切面编程,这里主要编写用于记录日志。
先pom.xml导入AOP的依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
快速入门程序:统计各个业务层方法执行耗时。
@Slf4j
@Component
@Aspect//表示这是一个AOP类
public class TimeAspect {
@Around(“execution(* top.hepingan.service.*.*(..))“) //切入点表达式
public Object recordTime(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
//记录开始时间
long begin=System.currentTimeMillis();
//调用原始方法运行
Object result=proceedingJoinPoint.proceed();
//记录结束时间 l
ong end=System.currentTimeMillis();
log.info(proceedingJoinPoint.getSignature()+“方法执行耗时:{}ms”,end–begin);
return result; }
}
@Around(“execution(* top.hepingan.service.*.*(..))”)环绕通知注解表示包括service层下的所有业务都要使用这个方法
直接通过案例来:先添加一个数据库表用于存储日志:
-- 操作日志表
create table operate_log(
id int unsigned primary key auto_increment comment 'ID',
operate_user int unsigned comment '操作人ID',
operate_time datetime comment '操作时间',
class_name varchar(100) comment '操作的类名',
method_name varchar(100) comment '操作的方法名',
method_params varchar(1000) comment '方法参数',
return_value varchar(2000) comment '返回值',
cost_time bigint comment '方法执行耗时, 单位:ms'
) comment '操作日志表';
然后再在pojo里创建一个Operate(操作的意思)类:用于定义数据库里面所有的列,并加上@Data等lombok注解。
然后再在top.hepingan下创建一个注解:anno.Log,里面编写:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
}
接着在Mapper里面创建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);
然后再在aop里面创建一个LogAspect:
@Aspect
@Component
@Slf4j
public class LogAscpect {
@Autowired
private OperateLogMapper operateLogMapper;
@Autowired
private HttpServletRequest httpServletRequest;
@Around("@annotation(top.hepingan.anno.Log)")
public Object recordLog(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
//获取JWT令牌并解析
String jwt=httpServletRequest.getHeader("token");
Claims claims= JwtUtils.parseJWT(jwt);//解析JWT令牌
Integer operateUser =(Integer) claims.get("id");
LocalDateTime operateTime=LocalDateTime.now();
//获取操作类名
String className=proceedingJoinPoint.getTarget().getClass().getName();
//获取方法名
String methodName=proceedingJoinPoint.getSignature().getName();
//获取方法参数
Object[] args=proceedingJoinPoint.getArgs();
String methodParams= Arrays.toString(args);
//方法执行耗时
long begin=System.currentTimeMillis();
//调用原始目标方法运行
Object result=proceedingJoinPoint.proceed();
//方法返回值
String returnValue= JSONObject.toJSONString(result);
long end=System.currentTimeMillis();
Long costTime=end-begin;
OperateLog operateLog=new OperateLog(null,operateUser,operateTime,className,methodName,methodParams,returnValue,costTime);
operateLogMapper.insert(operateLog);
log.info("AOP记录操作日志:{}",operateLog);
return result;
}
}
都有注释直接看就行,就是把数据库中的每一个对应的参数都写出来。
我们的目的是在增删改操作后都输出一段日志到数据库中,这时就直接在各个增删改方法上添加上@Log注解。