使用mybatis的@Interceptor实现拦截sql

一  mybatis的拦截器

1.1  拦截器介绍

拦截器是一种基于 AOP(面向切面编程)的技术,它可以在目标对象的方法执行前后插入自定义的逻辑。

1.2  语法介绍

1.注解@Intercepts

@Intercepts({@Signature(type = StatementHandler.class, method = “prepare”, args = {Connection.class, Integer.class})}),表示在 SQL 执行之前进行拦截处理。

@Intercepts 的作用:声明这是一个拦截器。

属性:Signature(注解)

@Signature:要拦截的具体方法

​ 属性: type-拦截接口(四种类型 ),

method-拦截的方法(update,insert,select),

args-重载时根据参数列表确定要拦截的方法。

2.介绍:

Executor:拦截执行器的方法,例如 update、query、commit、rollback 等。可以用来实现缓存、事务、分页等功能。
ParameterHandler:拦截参数处理器的方法,例如 setParameters 等。可以用来转换或加密参数等功能。
ResultSetHandler:拦截结果集处理器的方法,例如 handleResultSets、handleOutputParameters 等。可以用来转换或过滤结果集等功能。
StatementHandler:拦截语句处理器的方法,例如 prepare、parameterize、batch、update、query 等。可以用来修改 SQL 语句、添加参数、记录日志等功能。

1.3  API接口

1.intercept:主要是写我们具体业务逻辑,比如针对增删改sql语句添加更新日期。

2.plugin:生成代理对象

3.setProperties:设置拦截器属性

二  实现案例

2.1 结构

2.2 代码

package com.ljf.springboot.mybaits.demos.utils;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.plugin.*;
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.springframework.stereotype.Component;import java.text.DateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Properties;/*** 1. 打印mysql完整的执行语句* 2. 打印mysql语句执行时间* 这里我们拦截Executor里面的query和update方法*/
@Component
@Intercepts({@Signature(method = "query",type = Executor.class,args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),@Signature(type = Executor.class,method = "update",args = {MappedStatement.class, Object.class})
})
public class LogInterceptor implements Interceptor {/*** 是否显示语句的执行时间*/public static final String PROPERTIES_KEY_ENABLE_EXECUTOR_TIME = "enableExecutorTIme";public static final String ENABLE_EXECUTOR_TIME = "0"; // 显示private boolean enableExecutorTime = false;@Overridepublic Object intercept(Invocation invocation) throws Throwable {System.out.println("dddddd======");// 获取执行方法的MappedStatement参数,不管是Executor的query方法还是update方法,第一个参数都是MappedStatementMappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];Object parameter = null;if (invocation.getArgs().length > 1) {parameter = invocation.getArgs()[1];}String sqlId = mappedStatement.getId();BoundSql boundSql = mappedStatement.getBoundSql(parameter);Configuration configuration = mappedStatement.getConfiguration();long sqlStartTime = System.currentTimeMillis();Object re = invocation.proceed();long sqlEndTime = System.currentTimeMillis();// 打印mysql执行语句String sql = getSql(configuration, boundSql, sqlId);System.out.println(sql);// 打印mysql执行时间if (enableExecutorTime) {String sqlTimeLog = sqlId + " 方法对应sql执行时间:" + (sqlEndTime - sqlStartTime) + " ms";System.out.println(sqlTimeLog);}return re;}/*** 通过该方法决定要返回的对象是目标对象还是对应的代理* 不要想的太复杂,一般就两种情况:* <p>* 1. return target;  直接返回目标对象,相当于当前Interceptor没起作用,不会调用上面的intercept()方法* 2. return Plugin.wrap(target, this);  返回代理对象,会调用上面的intercept()方法** @param target 目标对象* @return 目标对象或者代理对象*/@Overridepublic Object plugin(Object target) {System.out.println("==================dssssssssss");return Plugin.wrap(target, this);}/*** 用于获取在Configuration初始化当前的Interceptor时时候设置的一些参数** @param properties Properties参数*/@Overridepublic void setProperties(Properties properties) {if (properties != null) {String executorTImeValue = properties.getProperty(PROPERTIES_KEY_ENABLE_EXECUTOR_TIME);if (executorTImeValue != null) {enableExecutorTime = executorTImeValue.equals(ENABLE_EXECUTOR_TIME);}}}private static String getSql(Configuration configuration, BoundSql boundSql, String sqlId) {return sqlId + " 方法对应sql执行语句:" + assembleSql(configuration, boundSql);}/*** 转义正则特殊字符 ($()*+.[]?\^{}* \\需要第一个替换,否则replace方法替换时会有逻辑bug*/private static String makeQueryStringAllRegExp(String str) {if (str != null && !str.equals("")) {return str.replace("\\", "\\\\").replace("*", "\\*").replace("+", "\\+").replace("|", "\\|").replace("{", "\\{").replace("}", "\\}").replace("(", "\\(").replace(")", "\\)").replace("^", "\\^").replace("$", "\\$").replace("[", "\\[").replace("]", "\\]").replace("?", "\\?").replace(",", "\\,").replace(".", "\\.").replace("&", "\\&");}return str;}/*** 获取参数对应的string值** @param obj 参数对应的值* @return string*/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 != null ? makeQueryStringAllRegExp(value) : value;}/*** 组装完整的sql语句 -- 把对应的参数都代入到sql语句里面** @param configuration Configuration* @param boundSql      BoundSql* @return sql完整语句*/private static String assembleSql(Configuration configuration, BoundSql boundSql) {// 获取mapper里面方法上的参数Object sqlParameter = boundSql.getParameterObject();// sql语句里面需要的参数 -- 真实需要用到的参数 因为sqlParameter里面的每个参数不一定都会用到List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();// sql原始语句(?还没有替换成我们具体的参数)String sql = boundSql.getSql().replaceAll("[\\s]+", " ");if (parameterMappings.size() > 0 && sqlParameter != null) {// sql语句里面的?替换成真实的参数TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();if (typeHandlerRegistry.hasTypeHandler(sqlParameter.getClass())) {sql = sql.replaceFirst("\\?", getParameterValue(sqlParameter));} else {MetaObject metaObject = configuration.newMetaObject(sqlParameter);for (ParameterMapping parameterMapping : parameterMappings) {// 一个一个把对应的值替换进去 按顺序把?替换成对应的值String propertyName = parameterMapping.getProperty();if (metaObject.hasGetter(propertyName)) {Object obj = metaObject.getValue(propertyName);sql = sql.replaceFirst("\\?", getParameterValue(obj));} else if (boundSql.hasAdditionalParameter(propertyName)) {Object obj = boundSql.getAdditionalParameter(propertyName);sql = sql.replaceFirst("\\?", getParameterValue(obj));}}}}return sql;}
}

2.3 验证效果

1.请求

2.日志结果

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

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

相关文章

vue-devtools 不显示的问题

vue 项目中浏览器右上角可以显示出插件&#xff0c;但是控制台却没有显示。 项目中在main.js中添加设置devtools为trueVue.config.devtools true插件显示可以使使用&#xff0c;控制台没有显示VUE选项 项目使用了 externals 打包优化&#xff0c;使用了vue的压缩版本&#x…

Verilog语法之case语句学习

case分支语句是一种实现多路分支控制的分支语句。与使用if-else条件分支语句相比&#xff0c;采用case分支语句来实现多路控制会变得更加的方便直观。 case分支语句通常用于对微处理器指令译码功能的描述以及对有限状态机的描述。Case分支语句有“case”、“casez”、“casex”…

Django创建多app应用

目录 1. 引言 2. 多app创建的两种方式 2.1 多个app结构 2.2 单个apps多个app 3. 最后 1. 引言 在平常业务开发中&#xff0c;我们遇到的功能可能会有很多&#xff0c;单个app的应用可能无法满足我们 这个时候&#xff0c;我们就需要多app应用&#xff0c;例如&#xff1a…

[RAM] 图解 RAM 结构原理

主页&#xff1a; 元存储博客 文章目录 前言1. Channel2. Dimm3. Rank4. Bank5. Row6. Column7. Beat8. Burst Length总结 前言 从CPU至DRAM晶粒之间依据层级由大至小为channel>DIMM>rank>chip>bank>row/column。 图片来源&#xff1a; 电脑王 DRAM层级关系 DR…

区块链dapp开发 dapp系统开发方案

在区块链技术的兴起和普及的推动下&#xff0c;去中心化应用程序&#xff08;DApp&#xff09;成为了当前数字世界中的热门话题之一。DApp 的开发不仅需要考虑技术方面的挑战&#xff0c;还需要深入了解区块链的工作原理和应用场景。本文将介绍一种 DApp 系统开发的基本方案&am…

SpringBoot登录校验(三)

​​​​​​​SpringBoot 登录认证&#xff08;一&#xff09;-CSDN博客 SpringBoot 登录认证&#xff08;二&#xff09;-CSDN博客 SpringBoot登录校验&#xff08;三&#xff09;-CSDN博客 前面我们介绍了传统的会话跟踪技术cookie和sesstion&#xff0c;本节讲解令牌技术…

Python数据分析必备工具——Pandas模块及其应用

Python数据分析必备工具——Pandas模块及其应用 外部数据的读取文本文件的读取语法示例 电子表格的读取语法示例 数据库数据的读取与操作语法 数据操作数据概述语法 数据筛选语法 数据清洗数据类型语法示例 沉余数据语法示例 异常值的识别与处理缺失值的识别与处理语法示例 数据…

Docker命令及部署Java项目

文章目录 简介Docker镜像镜像列表查找镜像拉取镜像删除镜像镜像标签 Docker容器容器启动容器查看容器停止和重启后台模式和进入强制停止容器清理停止的容器容器错误日志容器别名及操作 Docker部署Java项目 简介 Docker是一种容器化技术&#xff0c;可以帮助开发者轻松打包应用…

实时渲染是什么意思?实时渲染和离线渲染的区别

一、实时渲染是什么意思&#xff1f; 实时渲染是指在计算机程序运行时即时地生成图像和动画的过程&#xff0c;这种渲染技术通常用于网络游戏、虚拟现实和增强现实等需要实时交互的XR应用中。实时渲染需要在每秒内渲染数百万到数十亿个像素&#xff0c;以呈现出平滑的动画和交…

考研数学|武忠祥学习包搭配《660》和《880》

一、660、880、三大计算简单分析 660题 这本题册具有高难度、综合度和深度&#xff0c;属于高质量的题材。我建议不要在基础阶段就着手解决其中的660题&#xff0c;因为这可能会影响你的信心。相反&#xff0c;你可以在基础阶段完成一轮学习后&#xff0c;将这些题目留到强化…

从运营层面看财务管理:如何做好项目的财务预算

有效的项目运营是企业发展进步的主要活动&#xff0c;企业管理者的项目财务管理主要针对项目财务预算。项目财务预算管理是企业财务管理的一个分支&#xff0c;也是项目财务管理的核心部分&#xff0c;其表现形式为一个综合性的财务计划&#xff0c;主要包括预算编制、报告、执…

Ubuntu20安装python3.10

1、添加 deadsnakes PPA 到源列表 add-apt-repository ppa:deadsnakes/ppa apt update 2、安装 apt install python3.10 3设置默认版本为 Python3.10 查看所有python版本 ls -l /usr/bin/python* update-alternatives --install /usr/bin/python3 python3 /usr/bin/pytho…