当前位置: 首页 > news >正文

手写mybatis拦截器自动填充数据

文章目录

  • 🌞 Sun Frame:SpringBoot 的轻量级开发框架(个人开源项目推荐)
    • 🌟 亮点功能
    • 📦 spring cloud模块概览
      • 常用工具
    • 🔗 更多信息
    • 1.将sun-club-subject模块的登录拦截器放到sun-club-common包中
        • 1.将context包和LoginUtil放到common包中,这样其他的子模块也可以获取loginId
        • 2.修改一个bug,当不使用网关请求时,空指针异常 LoginInterceptor.java
    • 2.sun-club-infra 编写MybatisInterceptor.java 对通用字段进行填充
        • 1.MybatisInterceptor.java
        • 2.测试

🌞 Sun Frame:SpringBoot 的轻量级开发框架(个人开源项目推荐)

Sun Frame Banner

轻松高效的现代化开发体验

Sun Frame 是我个人开源的一款基于 SpringBoot 的轻量级框架,专为中小型企业设计。它提供了一种快速、简单且易于扩展的开发方式。

我们的开发文档记录了整个项目从0到1的任何细节,实属不易,请给我们一个Star!🌟
您的支持是我们持续改进的动力。

🌟 亮点功能

  • 组件化开发:灵活选择,简化流程。
  • 高性能:通过异步日志和 Redis 缓存提升性能。
  • 易扩展:支持多种数据库和消息队列。

📦 spring cloud模块概览

  • Nacos 服务:高效的服务注册与发现。
  • Feign 远程调用:简化服务间通信。
  • 强大网关:路由与限流。

常用工具

  • 日志管理:异步处理与链路追踪。
  • Redis 集成:支持分布式锁与缓存。
  • Swagger 文档:便捷的 API 入口。
  • 测试支持:SpringBoot-Test 集成。
  • EasyCode:自定义EasyCode模板引擎,一键生成CRUD。

🔗 更多信息

  • 开源地址:Gitee Sun Frame
  • 详细文档:语雀文档
    在这里插入图片描述

1.将sun-club-subject模块的登录拦截器放到sun-club-common包中

1.将context包和LoginUtil放到common包中,这样其他的子模块也可以获取loginId

image-20240621141949336

2.修改一个bug,当不使用网关请求时,空指针异常 LoginInterceptor.java

image-20240621142147066

2.sun-club-infra 编写MybatisInterceptor.java 对通用字段进行填充

1.MybatisInterceptor.java
package com.sunxiansheng.subject.infra.config;import com.sunxiansheng.subject.common.util.LoginUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.executor.Executor;
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.*;/*** Description: 填充created_by、created_time、update_by、update_time、is_deleted等公共字段的拦截器* @Author sun* @Create 2024/6/21 14:23* @Version 1.0*/
@Component
@Slf4j
// sql拦截器常规配置
@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class,Object.class
})})
public class MybatisInterceptor implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];// 获取sql的类型SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();// 获取sql的参数对象Object parameter = invocation.getArgs()[1];// 如果参数对象为空,就直接放行if (parameter == null) {return invocation.proceed();}// 获取当前用户登录的idString loginId = LoginUtil.getLoginId();if (StringUtils.isBlank(loginId)) {return invocation.proceed();}// 如果sql的类型是插入或者更新,对参数做替换,填充created_by、created_time、update_by、update_time、is_deletedif (SqlCommandType.INSERT == sqlCommandType || SqlCommandType.UPDATE == sqlCommandType) {replaceEntityProperty(parameter, loginId, sqlCommandType);}// 放行return invocation.proceed();}// 填充created_by、created_time、update_by、update_time、is_deletedprivate void replaceEntityProperty(Object parameter, String loginId, SqlCommandType sqlCommandType) {if (parameter instanceof Map) {// Map类型,就相当于批量插入的那种情况replaceMap((Map) parameter, loginId, sqlCommandType);} else {// 普通的eneity类型replace(parameter, loginId, sqlCommandType);}}private void replaceMap(Map parameter, String loginId, SqlCommandType sqlCommandType) {// 获取到所有的参数对象Collection values = parameter.values();for (Object value : values) {// 将每个参数对象都进行替换replace(value, loginId, sqlCommandType);}}/*** 填充数据的逻辑* @param parameter* @param loginId* @param sqlCommandType*/private void replace(Object parameter, String loginId, SqlCommandType sqlCommandType) {// 如果是插入类型的sqlif (SqlCommandType.INSERT == sqlCommandType) {dealInsert(parameter, loginId);} else {// 由于只有插入类型和更新类型的sql类型会走到这里,所以这里一定就是更新类型了dealUpdate(parameter, loginId);}}/*** 更新类型的sql,填充数据的方式,对update_by、update_time进行填充* @param parameter* @param loginId*/private void dealUpdate(Object parameter, String loginId) {// 获取这个对象对应类的所有字段Field[] allFields = getAllFields(parameter);// 遍历字段for (Field field : allFields) {// 使用try-catch捕捉异常,使其不影响主程序的执行try {// 设置这个字段为可访问的field.setAccessible(true);// 得到字段的值,后面指定对象Object o = field.get(parameter);if (Objects.nonNull(o)) {// 如果字段的值不是空的,将这个字段设置成不可访问的field.setAccessible(false);// 然后跳过这次循环,遍历下一个字段continue;}// 如果字段的值是空的,则根据字段的名字来填充数据if ("updateBy".equals(field.getName())) {// 设置为用户的loginIdfield.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("dealInsert.error:{}", e.getMessage(), e);}}}/*** 插入类型的sql,填充数据的方式,对created_by、created_time、is_deleted进行填充* @param parameter* @param loginId*/private void dealInsert(Object parameter, String loginId) {// 获取这个对象对应类的所有字段Field[] allFields = getAllFields(parameter);// 遍历字段for (Field field : allFields) {// 使用try-catch捕捉异常,使其不影响主程序的执行try {// 设置这个字段为可访问的field.setAccessible(true);// 得到字段的值,后面指定对象Object o = field.get(parameter);if (Objects.nonNull(o)) {// 如果字段的值不是空的,将这个字段设置成不可访问的field.setAccessible(false);// 然后跳过这次循环,遍历下一个字段continue;}// 如果字段的值是空的,则根据字段的名字来填充数据if ("isDeleted".equals(field.getName())) {// 默认设置为0,即未删除field.set(parameter, 0);// 设置字段为不可访问field.setAccessible(false);} else if ("createdBy".equals(field.getName())) {// 设置为用户的loginIdfield.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);}}}/*** 反射获取类的所有字段,并以字段数组的形式返回* @param object* @return*/private Field[] getAllFields(Object object) {// 获取当前对象对应类的Class对象Class<?> clazz = object.getClass();// 使用一个列表存储该类的所有字段信息List<Field> fieldList = new ArrayList<>();while (clazz != null) {// 将字段信息放到字段列表中fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));// clazz指向父类clazz = clazz.getSuperclass();}// 循环完成后,就会将当前的对象对应的类以及父类的字段对象放到字段列表中// 创建一个Field类型的数组,大小跟字段列表相同Field[] fields = new Field[fieldList.size()];// 将字段列表的元素放到数组中fieldList.toArray(fields);return fields;}@Overridepublic Object plugin(Object target) {// 返回一个代理对象,用于在方法调用时进行拦截处理return Plugin.wrap(target, this);}@Overridepublic void setProperties(Properties properties) {// 不需要任何处理}}
2.测试

image-20240621162204922

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Midjourney进阶-创建与管理自己的专属参数
  • vscode 目录管理
  • (南京观海微电子)——示波器使用介绍
  • Linux-Haproxy搭建Web群集
  • Android SurfaceFlinger——Vsync监听逻辑(五十三)
  • 重启人生计划-大梦方醒
  • lsync+nfs+rsync
  • TCP回显服务器
  • docker安装redis单机部署的redis.conf配置
  • 形态学处理方法
  • C++11中的Lambda表达式
  • os.path库学习之split函数
  • 【设计模式】六大基本原则
  • 前端已经学会vue,做粒子效果
  • MyBatis Plus 会在执行 SQL 查询时自动应用拦截器链,包括分页拦截器,从而简化分页逻辑的处理
  • Angular 响应式表单之下拉框
  • Create React App 使用
  • Effective Java 笔记(一)
  • ES10 特性的完整指南
  • express + mock 让前后台并行开发
  • Java新版本的开发已正式进入轨道,版本号18.3
  • LeetCode18.四数之和 JavaScript
  • Redux 中间件分析
  • SQLServer之创建显式事务
  • webpack4 一点通
  • 第2章 网络文档
  • 服务器从安装到部署全过程(二)
  • 强力优化Rancher k8s中国区的使用体验
  • 巧用 TypeScript (一)
  • 项目实战-Api的解决方案
  • 云大使推广中的常见热门问题
  • ​DB-Engines 12月数据库排名: PostgreSQL有望获得「2020年度数据库」荣誉?
  • ​软考-高级-系统架构设计师教程(清华第2版)【第12章 信息系统架构设计理论与实践(P420~465)-思维导图】​
  • # Kafka_深入探秘者(2):kafka 生产者
  • #《AI中文版》V3 第 1 章 概述
  • #【QT 5 调试软件后,发布相关:软件生成exe文件 + 文件打包】
  • #微信小程序:微信小程序常见的配置传值
  • (delphi11最新学习资料) Object Pascal 学习笔记---第7章第3节(封装和窗体)
  • (Java企业 / 公司项目)点赞业务系统设计-批量查询点赞状态(二)
  • (附源码)apringboot计算机专业大学生就业指南 毕业设计061355
  • (附源码)python房屋租赁管理系统 毕业设计 745613
  • (附源码)ssm经济信息门户网站 毕业设计 141634
  • (六)库存超卖案例实战——使用mysql分布式锁解决“超卖”问题
  • (十一)图像的罗伯特梯度锐化
  • (一)python发送HTTP 请求的两种方式(get和post )
  • (转)全文检索技术学习(三)——Lucene支持中文分词
  • .bat批处理(六):替换字符串中匹配的子串
  • .NET C# 使用 SetWindowsHookEx 监听鼠标或键盘消息以及此方法的坑
  • .net core + vue 搭建前后端分离的框架
  • .NET Framework、.NET Core 、 .NET 5、.NET 6和.NET 7 和.NET8 简介及区别
  • .NET 直连SAP HANA数据库
  • .Net 转战 Android 4.4 日常笔记(4)--按钮事件和国际化
  • .NET8使用VS2022打包Docker镜像
  • .NetCore部署微服务(二)
  • .NET框架设计—常被忽视的C#设计技巧