【工具】 MyBatis Plus的SQL拦截器自动翻译替换“?“符号为真实数值

news2024/11/25 3:45:04

【工具】 MyBatis Plus的SQL拦截器自动翻译替换"?"符号为真实数值

使用MyBatis的配置如下所示:

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

调用接口,sql日志打印如下:

image-20240531164616237

参数和sql语句不在同一行显示,需要自己代入思考,不够直观。

实现"?"的自动翻译,步骤如下:

1)编写sql语句拦截器:

import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Properties;

@Intercepts({
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class,
                Object.class}),
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class,
                Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class})})
public class SqlStatementInterceptor implements Interceptor {

    public static final Logger log = LoggerFactory.getLogger("sys-sql");

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        long startTime = System.currentTimeMillis();
        try {
            return invocation.proceed();
        } finally {
            long timeConsuming = System.currentTimeMillis() - startTime;
            log.info("执行SQL:{}ms", timeConsuming);
            if (timeConsuming > 999 && timeConsuming < 5000) {
                log.info("执行SQL大于1s:{}ms", timeConsuming);
            } else if (timeConsuming >= 5000 && timeConsuming < 10000) {
                log.info("执行SQL大于5s:{}ms", timeConsuming);
            } else if (timeConsuming >= 10000) {
                log.info("执行SQL大于10s:{}ms", timeConsuming);
            }
        }
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {

    }
}

2)编写MyBatisPlus日志拦截器:

import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;

import java.sql.SQLException;
import java.text.DateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.regex.Matcher;

public class MybatisPlusAllSqlLog implements InnerInterceptor {
    public static final Logger log = LoggerFactory.getLogger("sys-sql");

    @Override
    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        logInfo(boundSql, ms, parameter);
    }

    @Override
    public void beforeUpdate(Executor executor, MappedStatement ms, Object parameter) throws SQLException {
        BoundSql boundSql = ms.getBoundSql(parameter);
        logInfo(boundSql, ms, parameter);
    }

    private static void logInfo(BoundSql boundSql, MappedStatement ms, Object parameter) {
        try {
            log.info("parameter = " + parameter);
            // 获取到节点的id,即sql语句的id
            String sqlId = ms.getId();
            log.info("sqlId = " + sqlId);
            // 获取节点的配置
            Configuration configuration = ms.getConfiguration();
            // 获取到最终的sql语句
            String sql = getSql(configuration, boundSql, sqlId);
            log.info("完整的sql:{}", sql);
        } catch (Exception e) {
            log.error("异常:{}", e.getLocalizedMessage(), e);
        }
    }

    // 封装了一下sql语句,使得结果返回完整xml路径下的sql语句节点id + sql语句
    public static String getSql(Configuration configuration, BoundSql boundSql, String sqlId) {
        return sqlId + ":" + showSql(configuration, boundSql);
    }

    // 进行?的替换
    public static String showSql(Configuration configuration, BoundSql boundSql) {
        // 获取参数
        Object parameterObject = boundSql.getParameterObject();
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        // sql语句中多个空格都用一个空格代替
        String sql = boundSql.getSql().replaceAll("[\\s]+", " ");
        if (!CollectionUtils.isEmpty(parameterMappings) && parameterObject != null) {
            // 获取类型处理器注册器,类型处理器的功能是进行java类型和数据库类型的转换
            TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
            // 如果根据parameterObject.getClass()可以找到对应的类型,则替换
            if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                sql = sql.replaceFirst("\\?",
                        Matcher.quoteReplacement(getParameterValue(parameterObject)));
            } else {
                // MetaObject主要是封装了originalObject对象,提供了get和set的方法用于获取和设置originalObject的属性值,主要支持对JavaBean、Collection、Map三种类型对象的操作
                MetaObject metaObject = configuration.newMetaObject(parameterObject);
                for (ParameterMapping parameterMapping : parameterMappings) {
                    String propertyName = parameterMapping.getProperty();
                    if (metaObject.hasGetter(propertyName)) {
                        Object obj = metaObject.getValue(propertyName);
                        sql = sql.replaceFirst("\\?",
                                Matcher.quoteReplacement(getParameterValue(obj)));
                    } else if (boundSql.hasAdditionalParameter(propertyName)) {
                        // 该分支是动态sql
                        Object obj = boundSql.getAdditionalParameter(propertyName);
                        sql = sql.replaceFirst("\\?",
                                Matcher.quoteReplacement(getParameterValue(obj)));
                    } else {
                        // 打印出缺失,提醒该参数缺失并防止错位
                        sql = sql.replaceFirst("\\?", "缺失");
                    }
                }
            }
        }
        return sql;
    }

    // 如果参数是String,则添加单引号, 如果是日期,则转换为时间格式器并加单引号; 对参数是null和不是null的情况作了处理
    private static String getParameterValue(Object obj) {
        String value;
        if (obj instanceof String) {
            value = "'" + obj.toString() + "'";
        } else if (obj instanceof Date) {
            DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT,
                    DateFormat.DEFAULT, Locale.CHINA);
            value = "'" + formatter.format(new Date()) + "'";
        } else {
            if (obj != null) {
                value = obj.toString();
            } else {
                value = "";
            }
        }
        return value;
    }

}

3)编写MyBatisPlus配置:

@Configuration
public class MybatisConfiguration {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        mybatisPlusInterceptor.addInnerInterceptor(new MybatisPlusAllSqlLog());
        return mybatisPlusInterceptor;
    }

}

测试:

调用接口,sql日志如下所示:

image-20240531165929624

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

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

相关文章

Docker的数据管理(数据卷+数据卷容器)

文章目录 一、Docker的数据管理1、概述2、主要的技术&#xff08;三种数据挂载方式&#xff09;2.1、数据卷&#xff08;Volumes&#xff09;2.2、绑定挂载&#xff08;Bind mounts&#xff09;2.3、tmpfs挂载&#xff08;Tmpfs mounts&#xff09;2.4、之间的关系&#xff08;…

DBeaver连接Oracle报错:ORA-12514

Listener refused the connection with the following error:ORA-12514, TNS:listener does not currently know of service requested inconnect descriptor ———————————————— 1.报错信息2.配置正确结语 ———————————————— 如果是第一次连接Or…

「51媒体」媒体邀约全攻略

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 媒体邀约全攻略可以概括如下&#xff0c;以确保流程的清晰性和完整性&#xff1a; 一、前期准备 明确目标&#xff1a;清晰定义活动的目标&#xff0c;例如增加曝光度、提高知名度、塑造…

FinalShell 配置SSH密钥登陆

转载请标明出处&#xff1a;http://blog.csdn.net/donkor_/article/details/139355489 文章目录 前言生成密钥服务器配置公钥本地配置私钥存储私钥FinalShell配置 总结 前言 本机FinalShell 配置SSH密钥登陆服务器&#xff0c;这样就不再需要使用密码进行登陆了。由于FinalSh…

【2024新版】银系统源码/超市收银系统/智慧新零售/ERP进销存管理/线上商城/商户助手

>>>系统简述&#xff1a;本系统适用于超吃便利店&#xff0c;美妆母婴行业&#xff0c;服装鞋帽行业&#xff0c;食品零售行业&#xff0c;3C数码电子行业&#xff0c;食品生鲜等一切零售行业&#xff0c;产品功能角色介绍如下 合伙人&#xff1a;无限发展代理商和商…

说明白计算机网络之TCP的流量控制与拥塞控制之慢开始算法与拥塞避免算法

TCP的流量控制 利用滑动窗口实现流量控制 设A向B发送数据&#xff0c;连接建立时候&#xff0c;B告诉A自身的接收窗口大小&#xff0c;A的发送窗口大小不能超过接收方B的窗口大小 流量控制&#xff1a;发送方发送速率不要太快&#xff0c;要让接收方来得及接收。窗口大小的单…

相对论真的很难理解吗?其实一点也不难,原理就在你我身边!

相对论&#xff0c;一个听起来就充满神秘色彩的名词&#xff0c;它在科学界的地位举足轻重&#xff0c;被誉为现代物理的基石。或许你并不了解相对论&#xff0c;但大概率应该听说过。 不过对于大多数人来说&#xff0c;相对论似乎总是笼罩在一层难以穿透的迷雾之中&#xff0c…

未来工厂新篇章:大型工厂3D可视化技术引领工业新潮流

在科技日新月异的今天&#xff0c;大型工厂不再是我们印象中机器轰鸣、尘土飞扬的钢铁丛林&#xff0c;而是变成了智慧与效率并存的现代化生产中心。这一切的改变&#xff0c;都离不开一项革命性的技术——3D可视化。 一、何为大型工厂3D可视化&#xff1f; 大型工厂3D可视化&…

VRTK4.0学习——(一)

此开发基于Unity 6000.0.0f1 1.导入VRTK v4 Tilia Package Importer.unitypackage包 2.在PackManager中查看配置是否正确 3.点击 Window→Tilia→Package Importer 打开面板后点击 Add Scoped Registry 4.首先我们先将CameraRigs导入,CameraRigs中主要是启动VR头盔的功能&…

Web前端三大主流框:React、Vue 和 Angular

在当今快速发展的 Web 开发领域&#xff0c;选择合适的前端框架对于项目的成功至关重要。React、Vue 和 Angular 作为三大主流前端框架&#xff0c;凭借其强大的功能和灵活的特性&#xff0c;赢得了众多开发者的青睐。本文将对这三大框架进行解析&#xff0c;帮助开发者了解它们…

达梦数据库写文件的方式探索

0x01 前沿 这篇文章整体算是《达梦数据库手工注入笔记》的续集&#xff0c;达梦作为国内优秀的信创数据库&#xff0c;在关基单位中拥有越来越大的用户使用量。 通过SQL注入来写文件一直以来都是SQL注入漏洞深入利用的一种方式&#xff0c;对于不同的数据库通常写文件的方式也是…

Check Point 安全网关任意文件读取漏洞复现(CVE-2024-24919)

Check Point 安全网关任意文件读取漏洞复现(CVE-2024-24919) 1.漏洞描述 Check Point Security Gateways 是 Check Point Sofware 提供的一系列 网络安全Q解决方案。这些解决方案包括下一代防火墙(NGFW)、数据中心安全网关和 A1驱动的量子网关&#xff0c;旨在为企业提供针对…

【自然语言处理】Transformer中的一种线性特征

相关博客 【自然语言处理】【大模型】语言模型物理学 第3.3部分&#xff1a;知识容量Scaling Laws 【自然语言处理】Transformer中的一种线性特征 【自然语言处理】【大模型】DeepSeek-V2论文解析 【自然语言处理】【大模型】BitNet&#xff1a;用1-bit Transformer训练LLM 【自…

【RuoYi】实现二级页面跳转

一、前言 在我们写项目的时候&#xff0c;很多时候都会用到二级页面的跳转&#xff0c;RuoYi框架中也不例外&#xff0c;RuoYi虽然前端用的是vue&#xff0c;但是因为其有权限设置&#xff0c;在实现二级页面跳转的时候和普通的页面跳转有所区别&#xff0c;博主这里进行记录&a…

基于朴素贝叶斯算法的新闻类型预测,django框架开发,前端bootstrap,有爬虫有数据库

背景 在当今信息爆炸的时代&#xff0c;新闻内容的分类和预测对于用户个性化推荐和信息检索至关重要。基于朴素贝叶斯算法的新闻类型预测系统结合了机器学习和自然语言处理技术&#xff0c;能够根据新闻内容自动进行分类&#xff0c;提高新闻处理效率和准确性。采用Django框架…

LeetCode-47 全排列Ⅱ

LeetCode-47 全排列Ⅱ 题目描述解题思路代码说明 题目描述 给定一个可包含重复数字的序列 nums &#xff0c;按任意顺序 返回所有不重复的全排列。 示例 &#xff1a; 输入&#xff1a;nums [1,1,2]输出&#xff1a; [[1,1,2], [1,2,1], [2,1,1]] b站题目解读讲的不好&…

【机器学习】Transformer模型大小与性能探究

Transformer模型大小与性能&#xff1a;不仅仅是尺寸的问题 一、Transformer模型的挑战二、经验标度定律的局限性三、记忆过程与性能动态五、结论与展望 在人工智能和机器学习的领域里&#xff0c;模型的大小与性能之间的关系一直是研究人员关注的焦点。然而&#xff0c;最近的…

CUDA学习(2)

什么是CUDA CUDA&#xff08;Compute Unified Device Architecture&#xff09;&#xff0c;统一计算设备架构&#xff0c;英伟达推出的基于其GPU的通用高性能计算平台和编程模型。 借助CUDA&#xff0c;开发者可以充分利用英伟达GPU的强大计算能力加速各种计算任务。 软件生…

下载安装node.js,查看node.js版本

目录 一、下载安装node.js 二、查看node.js版本 三、使用nvm管理node.js版本 一、下载安装node.js 文档 nodejs中文网•学习教程•入门•如何安装 Nodejshttps://nodejs.cn/en/learn/getting-started/how-to-install-nodejs 步骤 1.进入node.js官网 nodejshttps://nodejs.…

解决:写路由文件时引入页面路径没有提示

1、首先安装插件&#xff1a; Path Autocomplete 2、配置settings.json 文件 复制下面代码到settings.json配置文件中&#xff1a; "path-autocomplete.pathMappings": {"": "${folder}/src/","views": "${folder}/src/views/&…