事务管理

事务

概念

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

操作

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

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

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

Spring事务管理

案例:解散部门,删除部门的同时要删除该部门下的员工

注解:@Transactional

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

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

在两次删除操作之间添加异常代码

    @Transactional@Overridepublic void delete(Integer id) {deptMapper.deleteById(id); //根据id删除部门int i = 1/0;empMapper.deleteByDeptId(id); //根据部门id删除员工}

再进行删除操作

这样就成功实现事务管理

事务进阶

rollbackFor

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

    @Transactional(rollbackFor = Exception.class)@Overridepublic void delete(Integer id) {deptMapper.deleteById(id); //根据id删除部门int i = 1/0;empMapper.deleteByDeptId(id); //根据部门id删除员工}

propagation

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

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

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

AOP基础

AOP概述

AOP:Aspect Oriented Programming(面向切面编程),其实是面向特定方法编程

场景:

实现:

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

AOP快速入门

统计业务层各个方法的执行耗时

1.导入AOP的相关依赖

2.编写AOP程序,针对特定方法根据业务进行编程

首先再pom文件中添加AOP的依赖

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

再编写AOP程序,新建一个aop.TimeAspect类

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;@Slf4j
@Component
@Aspect //AOP类
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进阶

通知类型

@PointCut注解:该注解的作用是将公共的切入点表达式抽取出来,需要用到是引入该切入点表达式即可

通知顺序

当多个切面的切入点都匹配到了目标方法,目标方法运行时,多个通知方法都会被运行

1.不同切面类中,默认按照不同切面类的类名字母排序

    目标方法前的通知方法:字母排序靠前的先执行

    目标方法后的通知方法:字母排序靠前的后执行

2.用 @Order(数字) 加在切面类上来控制顺序

    目标方法前的通知方法:数字小的先执行

    目标方法后的通知方法:数字小的后执行

切入点表达式

即描述切入点方法的一种表达式,主要用来决定项目中的哪些方法要加入通知

execution

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

带?的表示是可以省略的部分

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

2.包名.类名:可省略

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

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

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

     

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

    

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

@annotation

如果想要匹配DeptServiceImpl中的list()和delete(Integer id)方法,使用execution写切入点表达式比较复杂

@PointCut("execution(* com.itheima.service.DeptService.list())" || "execution(* com.itheima.service.DeptService.delete(java.lang.Integer))")
private void pt(){}

@annotation切入点表达式用于匹配标识有特定注解的方法

连接点

在Spring中使用JoinPoint抽象了连接点,用它可以获得方法执行时的相关信息,如目标类名、方法名、方法参数等

对于@Around通知,获取连接点信息只能使用ProceedingJoinPoint

对于其他四种通知,获取连接点信息只能使用JoinPoint,他是ProceedingJoinPoint的父类

AOP案例

需求:将案例中增、删、改相关接口的操作日志记录到数据库表中,日志信息包含:操作人、操作时间、执行方法的全类名、执行方法名、方法运行时参数、返回值、方法执行时长

思路:需要对所有业务类中的增、删、改 方法添加统一功能,使用AOP技术最为方便;由于增、删、改 方法名没有规律,可以自定义@Log注解完成目标方法匹配

首先在pom文件中添加AOP相关依赖

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

接着新建一个操作日志表

-- 操作日志表
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 '操作日志表';

再新建一个日志表对应的实体类

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; //IDprivate Integer operateUser; //操作人IDprivate LocalDateTime operateTime; //操作时间private String className; //操作类名private String methodName; //操作方法名private String methodParams; //操作方法参数private String returnValue; //操作方法返回值private Long costTime; //操作耗时
}

编写Log注解(@annotation切面表达式)

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 {
}

编写通知,定义切面类

package com.itheima.aop;import com.alibaba.fastjson.JSON;
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 io.jsonwebtoken.Jwts;
import jakarta.servlet.http.HttpServletRequest;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.time.LocalDateTime;
import java.util.Arrays;@Slf4j
@Component
@Aspect //切面类
public class LogAspect {@Autowiredprivate HttpServletRequest request;@Autowiredprivate OperateLogMapper operateLogMapper;@Around("@annotation(com.itheima.anno.Log)")public Object recordLog(ProceedingJoinPoint joinPoint) throws Throwable {//操作人id//获取请求头中的JWT令牌,解析令牌String jwt =  request.getHeader("token");Claims claims = JwtUtils.parseJWT(jwt);Integer operateUser = (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, operateUser, operateTime, className, methodName, methodParams, returnValue, costTime);operateLogMapper.insert(operateLog);log.info("AOP记录操作日志:{}",operateLog);return result;}
}

然后要在需要在目标对象(DeptServiceImpl、EmpServiceImpl)的连接点方法(增、删、改)上添加刚刚写的Log注解,执行启动类,进行一次新增部门操作和一次删除部门操作,日志成功记录在operate_log表中

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/99220.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/99220.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/99220.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

检索融合方法- Distribution-Based Score Fusion (DBSF)

在信息检索&#xff08;IR&#xff09;、推荐系统和多模态检索中&#xff0c;我们常常需要融合来自多个检索器或模型的结果。不同检索器可能对同一文档打出的分数差异很大&#xff0c;如果直接简单加权&#xff0c;很容易出现某个检索器“主导融合结果”的情况。 Distribution…

Oracle体系结构-归档日志文件(Archive Log Files)

核心概念&#xff1a;什么是归档日志文件&#xff1f; 定义&#xff1a; 归档日志文件&#xff08;Archive Log Files&#xff09;是在线重做日志文件&#xff08;Online Redo Log Files&#xff09;在被覆盖之前的一个完整副本。它们由 Oracle 的后台进程 ARCn&#xff08;归档…

GoogLeNet实战:用PyTorch实现经典Inception模块

配套笔记&讲解视频&#xff0c;点击文末名片获取研究背景&#xff08;Background&#xff09; 1.1 领域现状&#xff08;大环境与挑战&#xff09; 想象一下&#xff0c;你和朋友们在看一大堆照片——猫、狗、汽车、蛋糕&#xff0c;大家要把每张照片贴上标签。几年前&…

【开题答辩全过程】以 “旧书驿站”微信小程序的设计与开发为例,包含答辩的问题和答案

个人简介一名14年经验的资深毕设内行人&#xff0c;语言擅长Java、php、微信小程序、Python、Golang、安卓Android等开发项目包括大数据、深度学习、网站、小程序、安卓、算法。平常会做一些项目定制化开发、代码讲解、答辩教学、文档编写、也懂一些降重方面的技巧。感谢大家的…

【办公类-112-01】20250912家园每周沟通指导(Deepseek扩写完善+Python模拟点击鼠标自动发送给家长微信)

背景需求 孩子刚上小班,家长比较关心孩子情况(情绪、社交、吃饭等) 所以我每周五晚上和家长沟通一下孩子的情况。 操作流程 第一周(9月5日)是“适应周”,我添加了所有孩子的一位家长的微信号 23份全部是手打,足足写了4个小时。第一周案例多,所以写了很多,措辞酝酿后…

Spark专题-第一部分:Spark 核心概述(1)-Spark 是什么?

众所周知&#xff0c;教学文档总该以理论部分作为开篇&#xff0c;于是我们这篇Spark专题同样会以一堆理论和专有名词开始&#xff0c;笔者会尽可能的让专业词汇通俗易懂 第一部分&#xff1a;Spark 核心概述 Spark 是什么&#xff1f; 1. 大数据时代的"超级赛车"…

从零到一上手 Protocol Buffers用 C# 打造可演进的通讯录

一、为什么是 Protobuf&#xff08;而不是 XML/自定义字符串/.NET 二进制序列化&#xff09; 在需要把结构化对象持久化或跨进程/跨语言传输时&#xff0c;常见方案各有痛点&#xff1a; BinaryFormatter 等 .NET 二进制序列化&#xff1a;对类型签名与版本极其脆弱、体积偏大&…

计算机网络(三)网络层

三、网络层网络层是五层模型中的第三层&#xff0c;位于数据链路层和传输层之间。它的核心任务是实现数据包在不同网络之间&#xff08;跨网络&#xff09;的逻辑传输。网络层的数据传输单位是数据报&#xff08;Datagram&#xff09;或数据包&#xff08;Packet&#xff09;。…

互联网大厂Java面试实录:从基础到微服务全栈技术答疑

互联网大厂Java面试实录&#xff1a;从基础到微服务全栈技术答疑 本文以电商场景为背景&#xff0c;展现一场互联网大厂Java开发职位的面试过程。严肃的面试官与搞笑的水货程序员谢飞机展开三轮技术问答&#xff0c;涵盖Java SE、Spring Boot、数据库、微服务、安全以及CI/CD等…

StringBuilder 深度解析:数据结构与扩容机制的底层细节

文章目录 前言 一、数据结构&#xff1a;不止是简单的字符数组 1. 核心成员变量&#xff08;定义在 AbstractStringBuilder 中&#xff09; 2. 构造器与初始容量 二、扩容机制&#xff1a;从 "不够用" 到 "换大容器" 的全过程 步骤 1&#xff1a;计算…

Elasticsearch面试精讲 Day 17:查询性能调优实践

【Elasticsearch面试精讲 Day 17】查询性能调优实践 在“Elasticsearch面试精讲”系列的第17天&#xff0c;我们聚焦于查询性能调优实践。作为全文检索与数据分析的核心引擎&#xff0c;Elasticsearch的查询性能直接影响用户体验和系统吞吐能力。在高并发、大数据量场景下&…

WPF 数据绑定模式详解(TwoWay、OneWay、OneTime、OneWayToSource、Default)

在WPF中&#xff0c;数据绑定模式&#xff08;Binding Mode&#xff09;用于指定数据流的方向。常见的模式有TwoWay、OneWay、OneTime、OneWayToSource和Default。TwoWay&#xff08;双向绑定&#xff09;&#xff1a;数据从源&#xff08;通常是ViewModel或数据上下文&#xf…

使用 NVIDIA Dynamo 部署 PD 分离推理服务

1 Dynamo 介绍 NVIDIA Dynamo 是一个开源的模块化推理框架&#xff0c;用于在分布式环境上实现生成式 AI 模型的服务化部署。Dynamo 通过动态资源调度、智能路由、内存优化与高速数据传输&#xff0c;无缝扩展大型 GPU 集群之间的推理工作负载。 Dynamo 采用推理引擎无关的设…

答题卡识别改分项目

目录 核心思路 分步实现与代码解析 1. 环境准备与工具函数定义 2. 图片预处理 3. 轮廓提取与筛选 3. 轮廓提取与筛选 4. 透视变换&#xff08;矫正倾斜答题卡&#xff09; 5. 阈值处理&#xff08;突出填涂区域&#xff09; 6. 提取选项圆圈轮廓 7. 选项轮廓排序&…

Python爬虫实战:研究Pandas,构建新浪网股票数据采集和分析系统

1. 系统概述 股票数据分析系统旨在通过自动化手段获取市场数据,进行深度分析,辅助投资决策。本系统主要包含以下核心模块: 数据爬取模块:从新浪财经获取股票列表、基本信息及历史交易数据 数据处理模块:清洗原始数据,处理缺失值与异常值,计算技术指标 分析可视化模块:…

【C++STL】list的详细用法和底层实现

&#x1f31f;个人主页&#xff1a;第七序章 &#x1f308;专栏系列&#xff1a;C&#xff0b;&#xff0b; 目录 ❄️前言&#xff1a; &#x1f308;一&#xff1a;介绍 &#x1f308;二&#xff1a;list的创建 ☀️基本框架 &#x1f319;节点类 &#x1f319;构造函…

AI大模型开发(多模态+提示词)

接着之前的例子&#xff0c;继续测试模型对话&#xff0c;今天主要测试多模态加上系统提示词。 一.多模态 多模态方法&#xff0c;主要添加了对图片的测试。 public String chatWithMessage(UserMessage userMessage){ChatResponse chatResponse qwenChatModel.chat(userMess…

Qt程序单独运行报错问题

Qt程序单独运行报错问题介绍问题原因分析解决方案&#xff08;从最佳实践到临时方法&#xff09;方法一&#xff1a;使用 windeployqt 工具&#xff08;最推荐、最规范&#xff09;方法二&#xff1a;临时修改系统 PATH&#xff08;适合开发调试&#xff09;方法三&#xff1a;…

Flask学习笔记(二)--路由和变量

一、路由Flask支持两种路由1、使用route()装饰器将URL绑定到函数app.route(/hello)def hello_world():return hello world2、使用应用程序对象的add_url_rule()函数def hello_world():return hello worldapp.add_url_rule(/, hello, hello_world)二、变量规则Flask开发中&#…

Skywalking告警配置+简易邮件告警应用配置(保姆级)

Skywalking告警配置简易邮件告警应用配置前言&#xff1a; 前文&#xff1a;SkyWalking Elasticsearch8 容器化部署指南&#xff1a;国内镜像加速与生产级调优_skywalkinges-CSDN博客 ​ SKywalking Agent配置Oracle监控插件安装指南-CSDN博客 Skywalking版本&#xff1a;V10.…