欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 旅游 > 手写mybatis拦截器自动填充数据

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

2025/4/22 4:54:17 来源:https://blog.csdn.net/m0_64637029/article/details/141102797  浏览:    关键词:手写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

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词