教人做衣服得网站有哪些/武汉网站推广公司

教人做衣服得网站有哪些,武汉网站推广公司,前端培训机构出来的好找工作吗,淘宝做促销的网站在本篇文章中,我们将深入了解如何编写一个 MyBatis 拦截器,并通过一个示例来展示如何在执行数据库操作(如插入或更新)时,自动填充某些字段(例如 createdBy 和 updatedBy)信息。本文将详细讲解拦…

在本篇文章中,我们将深入了解如何编写一个 MyBatis 拦截器,并通过一个示例来展示如何在执行数据库操作(如插入或更新)时,自动填充某些字段(例如 createdByupdatedBy)信息。本文将详细讲解拦截器的工作原理、代码示例及其在 MyBatis 项目中的应用。

一、为什么需要数据库操作拦截器?

在典型的企业级应用开发中,我们经常会遇到这样的需求:

  1. 需要自动记录数据行的创建时间和修改时间

  2. 需要跟踪记录数据操作人

  3. 需要实现全局的软删除逻辑

  4. 需要对敏感数据进行自动加密

  5. 需要实现SQL执行监控和慢查询统计

传统做法是在每个Mapper方法中手动添加这些逻辑,但这样会导致大量重复代码。MyBatis拦截器(Interceptor)正是为了解决这类问题而生,它可以在SQL执行的各个阶段插入自定义逻辑,实现横切关注点的统一管理。

二、MyBatis拦截器核心原理

如果对于MyBatis核心组件功能还不了解的小伙伴们建议先跳到最后一章节:第五节去了解MyBatis核心组件功能再倒回来继续学习!

2.1 拦截器架构

MyBatis采用责任链模式实现拦截器机制,主要拦截点包括:

拦截接口拦截时机典型应用场景
ExecutorSQL执行前后(增删改查操作)事务管理、分页处理
StatementHandlerSQL语句构建时SQL改写、危险操作拦截
ParameterHandler参数处理时参数加密、参数校验
ResultSetHandler结果集处理时结果解密、数据脱敏

2.2 拦截器生命周期

MyBatis 中的拦截器生命周期较为简单。它的生命周期由 Plugin.wrap() 方法控制,首先会创建代理对象并包装目标对象。拦截器的 intercept 方法在执行目标方法前被调用,拦截器的 plugin 方法用于对目标方法的代理,setProperties 方法用于注入拦截器所需的配置属性。

拦截目标方法调用的顺序:当拦截器装载到 MyBatis 配置中后,每次执行目标方法(如数据库操作的 insertupdate)时,都会经过拦截器链。如果多个拦截器存在,它们会按照配置顺序依次执行。可以通过实现Ordered接口或使用@Order注解控制执行顺序:

@Intercepts(...)
@Order(Ordered.HIGHEST_PRECEDENCE)
public class FirstInterceptor implements Interceptor {}@Intercepts(...)
@Order(Ordered.LOWEST_PRECEDENCE)
public class LastInterceptor implements Interceptor {}
关键接口解析
  1. 初始化阶段:通过@Intercepts注解声明拦截目标

  2. 代理阶段:通过plugin()方法创建代理对象

  3. 执行阶段:intercept()方法处理拦截逻辑

  4. 配置阶段:通过XML或Java Config注册拦截器

// 典型拦截器声明
@Intercepts({@Signature(type = Executor.class, method = "update",args = {MappedStatement.class, Object.class})
})
public class CustomInterceptor implements Interceptor {// 实现方法...
}

2.3 代理模式与责任链模式

  • 代理模式(Proxy Pattern):MyBatis 拦截器本质上是通过代理模式对目标对象进行包装,通过 Plugin.wrap() 方法将拦截器包装在目标对象上,形成一个代理对象。这个代理对象会拦截对目标方法的调用,执行预定的增强逻辑,再将控制权交给目标方法。
  • 责任链模式(Chain of Responsibility Pattern):MyBatis 的拦截器是按顺序进行链式调用的,多个拦截器会组成一个链,按照声明顺序依次执行,直到所有拦截器都被调用。

三、实战:实现自动化字段填充

3.1 需求分析

实现以下字段的自动填充:

字段名插入时自动填充更新时自动填充数据类型
created_by✔️String
created_time✔️Date
updated_by✔️String
updated_time✔️Date
is_deleted✔️(默认0)Integer

 3.2 完整实现代码解析

package com.wanren.subject.application.interceptor;import com.wanren.subject.common.util.LoginUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.springframework.stereotype.Component;import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.Executor;/***@ClassName MybatisInterceptor*@Description 填充createBy,createTime等公共字段的拦截器*@Author 彭于晏*Date 2025/2/14 0:31*Version 1.0**/
@Component
@Slf4j
//@Signature指明该拦截器需要拦截哪一个接口的哪一个方法,包括如下
// 接口类型:Executor(拦截执行器方法,负责调用StatementHandler操作数据库)
// StatementHandler(//拦截SQL语法构建处理,直接在数据库执行SQL脚本的对象)、
// ParameterHandler(//拦截参数处理)和ResultSetHandler(//拦截结果集处理,ResultSet结果集对象转换成List类型的集合)
//对应接口中的某一个方法的参数,比如Executor中query方法因为重载原因,有多个,args就是指明参数类型,从而确定是具体哪一个方法。
@Intercepts(@Signature(type = Executor.class,method = "update",args = {MappedStatement.class,Object.class
}))
public class MybatisInterceptor implements Interceptor {//当被拦截的数据库操作(如查询、插入、更新)发生时,intercept 方法会被调用。//invocation的三个方法:Object target = invocation.getTarget();//被代理对象//Method method = invocation.getMethod();//代理方法//Object[] args = invocation.getArgs();//方法参数@Overridepublic Object intercept(Invocation invocation) throws Throwable {MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];//MyBatis 中封装 SQL 语句信息的类,表示一个 SQL 映射。MappedStatement 对象包含 SQL 执行所需的所有信息,比如 SQL 命令类型、SQL 语句、参数类型等SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();Object parameter = invocation.getArgs()[1];//parameter 是一个 Object 类型,通常是实体类对象if(parameter == null){//执行目标方法(invocation.proceed())return invocation.proceed();}//获取当前登录用户的idString loginId = LoginUtil.getLoginId();if(StringUtils.isBlank(loginId)){return invocation.proceed();}if(sqlCommandType.INSERT == sqlCommandType || sqlCommandType.UPDATE == sqlCommandType){replaceEntityProperty(parameter,loginId,sqlCommandType);}return invocation.proceed();}private void replaceEntityProperty(Object parameter, String loginId, SqlCommandType sqlCommandType) {if(parameter instanceof Map){replaceMap((Map) parameter,loginId,sqlCommandType);}else{replace(parameter,loginId,sqlCommandType);}}private void replace(Object parameter, String loginId, SqlCommandType sqlCommandType) {if(SqlCommandType.INSERT == sqlCommandType){dealInsert(parameter,loginId);}else{dealUpdate(parameter,loginId);}}private void dealUpdate(Object parameter, String loginId) {Field[] fields = getAllFields(parameter);for (Field field : fields) {try {field.setAccessible(true);//parameter是字段名,field是字段的值Object o = field.get(parameter);//如果该字段不为null,则跳过if (Objects.nonNull(o)) {field.setAccessible(false);continue;}if ("updateBy".equals(field.getName())) {field.set(parameter, loginId);field.setAccessible(false);} else if ("updateTime".equals(field.getName())) {field.set(parameter, new Date());field.setAccessible(false);} else {field.setAccessible(false);}}catch (Exception e){log.error("dealUpdate.error:{}", e.getMessage(), e);}}}private void dealInsert(Object parameter, String loginId) {Field[] fields = getAllFields(parameter);for(Field field : fields){try{//Java 语言的私有字段是不可访问的。如果你想访问私有字段(即在类外部访问 private 字段),// 你必须调用 setAccessible(true) 来打破 Java 的访问控制,允许访问这些字段。field.setAccessible(true);Object o = field.get(parameter);if (Objects.nonNull(o)) {field.setAccessible(false);continue;}if("isDeleted".equals(field.getName())){field.set(parameter,0);field.setAccessible(false);}else if ("createdBy".equals(field.getName())) {field.set(parameter, loginId);field.setAccessible(false);} else if ("createdTime".equals(field.getName())) {field.set(parameter, new Date());field.setAccessible(false);}else {field.setAccessible(false);}}catch (Exception e){log.error("dealInsert.error:{}", e.getMessage(), e);}}}private Field[] getAllFields(Object parameter) {Class<?> clazz = parameter.getClass();List<Field> fieldList = new ArrayList<>();while (clazz != null){fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));clazz = clazz.getSuperclass();}Field[] fields =new Field[fieldList.size()];fieldList.toArray(fields);return fields;}private void replaceMap(Map parameter, String loginId, SqlCommandType sqlCommandType) {for(Object val : parameter.values()){replace(val,loginId,sqlCommandType);}}//插件用于封装目标对象,通过这个方法可以返回目标对象本身,也可以返回一个他的代理,决定//是否要进行拦截,从而进一步决定要返回一个怎样的对象@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}//如果我们拦截器需要用到一些变量参数,而且这个参数是支持可配置的,类似Spring中的@Value("${}")从application.properties文件获取自定义变量属性,这个时候我们就可以使用这个方法。//在拦截器插件的setProperties方法中进行。这些自定义属性参数会在项目启动的时候被加载。@Overridepublic void setProperties(Properties properties) {}
}

四、扩展应用场景

4.1 敏感数据加密

public Object intercept(Invocation invocation) {Object parameter = invocation.getArgs()[1];if (parameter instanceof User) {User user = (User) parameter;user.setPassword(encrypt(user.getPassword()));}return invocation.proceed();
}

4.2 SQL执行监控 

public Object intercept(Invocation invocation) {long start = System.nanoTime();try {return invocation.proceed();} finally {long cost = (System.nanoTime() - start)/1000000;if(cost > 1000){log.warn("慢查询警告: {}ms - {}", cost, getSql(invocation));}Metrics.counter("sql.total.count").increment();Metrics.timer("sql.execute.time").record(cost, MILLISECONDS);}
}

4.3 多租户数据隔离

public void replaceEntityProperty(Object parameter, String tenantId) {if (parameter instanceof TenantAware) {((TenantAware) parameter).setTenantId(tenantId);}
}

 五、MyBatis核心组件功能详解

从MyBatis代码实现的角度来看,MyBatis的核心组成部分涉及到多个组件和接口,它们共同协作完成了数据库操作的管理、映射及优化等工作。以下是MyBatis的核心部件及其功能的详细介绍:

1. Configuration(配置对象)

功能概述
Configuration 是 MyBatis 的全局配置中心,负责管理所有配置信息,包括数据库连接、映射器、类型处理器、插件等。它是 MyBatis 初始化的核心类。

核心职责

  • 加载并解析 MyBatis 配置文件(如 mybatis-config.xml)。

  • 管理 Mapper 文件和接口的配置信息。

  • 注册类型处理器(TypeHandler)和对象工厂(ObjectFactory)。

  • 维护拦截器链(InterceptorChain),用于支持插件扩展。

2. SqlSessionFactory(会话工厂)

功能概述
SqlSessionFactory 是 MyBatis 的核心工厂类,用于创建 SqlSession 实例。它是线程安全的,通常在应用启动时初始化。

核心职责

  • 根据 Configuration 创建 SqlSession

  • 管理数据库连接池和事务工厂。

  • 加载 Mapper 文件和对应的映射语句。

3. SqlSession(会话)

功能概述
SqlSession 是 MyBatis 与数据库交互的核心接口,提供了执行 SQL、管理事务、获取 Mapper 接口等功能。

核心职责

  • 执行 CRUD 操作(selectinsertupdatedelete)。

  • 提供事务管理、批量操作、缓存等功能。

  • 获取 Mapper 接口的代理对象。

4. Executor(执行器)

功能概述
Executor 是 MyBatis 的内部执行器,负责执行 SQL 语句并处理结果。它是 MyBatis 中的核心对象之一,负责查询、插入、更新等数据库操作。

核心职责

  • 调用 StatementHandler 执行 SQL 语句。

  • 管理一级缓存和二级缓存。

  • 触发拦截器链,支持插件扩展。

5. StatementHandler(语句处理器)

功能概述

StatementHandler 负责将 SQL 语句发送到数据库执行,主要完成 SQL 的解析、生成和执行。

核心职责

  • 创建 PreparedStatement 并设置参数。

  • 执行 SQL 并返回结果。

  • 支持一级缓存,避免重复查询。

6. ParameterHandler(参数处理器)

功能概述
ParameterHandler 负责将 Java 对象转换为 JDBC 参数,并设置到 PreparedStatement 中。

核心职责

  • 处理 SQL 参数的映射。

  • 将 Java 对象转换为 JDBC 所需的参数格式。

7. ResultSetHandler(结果集处理器)

功能概述
ResultSetHandler 负责将 JDBC 返回的 ResultSet 转换为 Java 对象集合。

核心职责

  • 解析 ResultSet 结果集。

  • 将数据库查询结果映射为 Java 对象。

8. TypeHandler(类型处理器)

功能概述
TypeHandler 用于 Java 类型和数据库类型之间的转换。负责将 Java 对象的属性值转换为数据库支持的数据类型,或者将数据库查询结果转化为 Java 对象。

核心职责

  • 处理 Java 类型与 JDBC 类型的映射。

  • 支持自定义类型转换。

9. MappedStatement(映射语句)

功能概述
MappedStatement负责封装单个 <select>、<insert>、<update>、<delete> 等 SQL 语句的配置信息。包括 SQL 类型、参数映射、结果映射等。

核心职责

  • 存储 SQL 语句、参数映射、返回值映射等信息。

  • 提供 SQL 执行的上下文信息。

10. SqlSource(SQL 源)

功能概述
SqlSource 负责根据传入的 parameterObject 动态生成 SQL 语句。它通过 BoundSql 对象封装 SQL 和参数,并返回给 StatementHandler 执行。

核心职责

  • 动态生成 SQL 语句。

  • 根据传入的参数对象构建 SQL 语句,并封装到 BoundSql 中。

11. BoundSql(封装的 SQL)

功能概述

BoundSql 负责封装动态生成的 SQL 语句和参数信息。它是 SqlSource 生成 SQL 后的载体,存储了 SQL 语句及其相关参数信息。

核心职责

  • 存储动态生成的 SQL 语句。

  • 提供 SQL 执行所需的参数信息。

"优秀的框架设计应该像电路板一样,允许开发者在不修改核心逻辑的情况下,通过插件机制扩展功能。" —— MyBatis设计哲学

 

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

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

相关文章

137,【4】 buuctf web [SCTF2019]Flag Shop

进入靶场 都点击看看 发现点击work会增加&#xffe5; 但肯定不能一直点下去 抓包看看 这看起来是一个 JWT&#xff08;JSON Web Token&#xff09;字符串。JWT 通常由三部分组成&#xff0c;通过点&#xff08;.&#xff09;分隔&#xff0c;分别是头部&#xff08;Header&…

twisted实现MMORPG 游戏数据库操作封装设计与实现

在设计 MMORPG&#xff08;大规模多人在线角色扮演游戏&#xff09;时&#xff0c;数据库系统是游戏架构中至关重要的一部分。数据库不仅承担了游戏中各种数据&#xff08;如玩家数据、物品数据、游戏世界状态等&#xff09;的存储和管理任务&#xff0c;还必须高效地支持并发访…

【R语言】聚类分析

聚类分析是一种常用的无监督学习方法&#xff0c;是将所观测的事物或者指标进行分类的一种统计分析方法&#xff0c;其目的是通过辨认在某些特征上相似的事物&#xff0c;并将它们分成各种类别。R语言提供了多种聚类分析的方法和包。 方法优点缺点适用场景K-means计算效率高需…

超全Deepseek资料包,deepseek下载安装部署提示词及本地部署指南介绍

该资料包涵盖了DeepSeek模型的下载、安装、部署以及本地运行的详细指南&#xff0c;适合希望在本地环境中高效运行DeepSeek模型的用户。资料包不仅包括基础的安装步骤&#xff0c;还提供了68G多套独立部署视频教程教程&#xff0c;针对不同硬件配置的模型选择建议&#xff0c;以…

python旅游推荐系统+爬虫+可视化(协同过滤算法)

✅️基于用户的协同过滤算法 ✅️有后台管理 ✅️2w多数据集 这个旅游数据分析推荐系统采用了Python语言、Django框架、MySQL数据库、requests库进行网络爬虫开发、机器学习中的协同过滤算法、ECharts数据可视化技术&#xff0c;以实现从网站抓取旅游数据、个性化推荐和直观展…

HarmonyNext上传用户相册图片到服务器

图片选择就不用说了&#xff0c;直接用 无须申请权限 。 上传图片&#xff0c;步骤和android对比稍微有点复杂&#xff0c;可能是为了安全性考虑&#xff0c;需要将图片先拷贝到缓存目录下面&#xff0c;然后再上传&#xff0c;当然你也可以转成Base64&#xff0c;然后和服务…

同为科技智能PDU助力Deepseek人工智能和数据交互的快速发展

1 2025开年&#xff0c;人工智能领域迎来了一场前所未有的变革。Deepseek成为代表“东方力量”的开年王炸&#xff0c;不仅在国内掀起了技术热潮&#xff0c;并且在全球范围内引起了高度关注。Deepseek以颠覆性技术突破和现象级应用场景席卷全球&#xff0c;这不仅重塑了产业格…

二、QEMU NFS 环境搭建

​ 在上一章节中&#xff0c;我们已经成功完成了内核和 busybox 环境的配置。为了进一步提高开发效率&#xff0c;我们可以使用 NFS&#xff08;Network File System&#xff09;来挂载根目录。NFS 允许我们将本地文件系统通过网络共享给虚拟机使用&#xff0c;这样在开发过程中…

瑞萨RA-T系列芯片ADCGPT功能模块的配合使用

在马达或电源工程中&#xff0c;往往需要采集多路AD信号&#xff0c;且这些信号的优先级和采样时机不相同。本篇介绍在使用RA-T系列芯片建立马达或电源工程时&#xff0c;如何根据需求来设置主要功能模块ADC&GPT&#xff0c;包括采样通道打包和分组&#xff0c;GPT触发启动…

20250217 随笔 redis非原子性操作简述

从你提供的文本来看&#xff0c;核心是 Redis 作为缓存的检查机制&#xff0c;以及非原子性操作导致的不一致性问题。 我们可以拆解为两个部分来理解&#xff1a; &#x1f4cc; 1. 逻辑&#xff1a;先查 Redis&#xff0c;再决定是否注册 逻辑流程 先查询 Redis 是否有某个 …

git-提交时间和作者时间的区别

1.介绍 定义介绍 提交时间&#xff08;Committer Date&#xff09;&#xff1a;决定了提交在 Git 历史中的位置&#xff0c;通常影响 GitHub 上提交显示的顺序。 作者时间&#xff08;Author Date&#xff09;&#xff1a;虽然不影响提交的排序&#xff0c;但在每个提交详情页…

Leetcode 146 LRU缓存 的三种解法

146. LRU 缓存 请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 实现 LRUCache 类&#xff1a; LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存int get(int key) 如果关键字 key 存在于缓存中&#xff0c;则返回关键字的值&#xff0…

尚硅谷 java 学习Day19 抽象类与抽象方法、接口、内部类

6-5 抽象类(abstract)与抽象方法&#xff08;important&#xff09; 一、什么叫抽象类&#xff1a; 有时候将一个父类设计的非常抽象&#xff0c;以至于它没有具体的实例&#xff0c;这样的类称为抽象类 abstract关键字的使用&#xff1a; ​ 1、abstract:抽象的 ​ 2、abs…

【Python pro】基本数据类型

一、数字类型 1.1 数字类型的组成 1.1.1 整数 &#xff08;1&#xff09;十进制&#xff0c;二进制0b&#xff0c;八进制0o&#xff0c;十六进制0x print(16 0b10000 0o20 0x10) # 输出&#xff1a;True&#xff08;2&#xff09;十进制转其他进制 a bin(16) b oct(1…

拯救者电脑在重装系统之后电源计划丢失Fn+Q切换不了模式怎么恢复?

参考联想知识库的一下链接&#xff1a; https://iknow.lenovo.com.cn/detail/196192 其中下载的解压文件后的文件需要复制粘贴到D盘的根目录下&#xff0c;再来运行文件。若在生成的log文件中看到导入成功以及控制面板中看到已添加的电源计划即可 如果还是无效可是试试以下的…

ubuntu 执行 sudo apt-get update 报错

记录一下&#xff0c;遇到这个问题了&#xff0c;网络上看到的解决办法&#xff0c;亲测有效 执行sudo apt-get update ,却报以下错误&#xff0c;“SECURITY: URL redirect target contains control characters rejecting ” 经检查发现&#xff0c;/etc/apt/source.list 下的…

深度集成DeepSeek大模型:WebSocket流式聊天实现

目录 5分钟快速接入DeepSeek大模型&#xff1a;WebSocket实时聊天指南创建应用开发后端代码 (Python/Node.js)结语 5分钟快速接入DeepSeek大模型&#xff1a;WebSocket实时聊天指南 创建应用 访问DeepSeek官网 前往 DeepSeek官网。如果还没有账号&#xff0c;需要先注册一个。…

java断点调试(debug)

在开发中&#xff0c;新手程序员在查找错误时, 这时老程序员就会温馨提示&#xff0c;可以用断点调试&#xff0c;一步一步的看源码执行的过程&#xff0c;从而发现错误所在。 重要提示: 断点调试过程是运行状态&#xff0c;是以对象的运行类型来执行的 断点调试介绍 断点调试是…

软件技术实训室解决方案(2025年最新版)

软件产业作为新兴产业的核心组成部分&#xff0c;是推动数字经济发展的重要力量。在“十四五”规划的新机遇与挑战下&#xff0c;我国已明确将加强关键数字技术创新应用作为战略重点&#xff0c;并将软件和信息技术服务业的发展列为重中之重。这不仅是为了加速构建现代产业体系…

foobar2000设置DSP使用教程及软件推荐

foobar2000安卓中文版&#xff1a;一款高品质手机音频播放器 foobar2000安卓中文版是一款备受好评的高品质手机音频播放器。 几乎支持所有的音频格式&#xff0c;包括 MP3、MP4、AAC、CD 音频等。不论是经典老歌还是最新的流行音乐&#xff0c;foobar2000都能完美播放。除此之…