【MybBatis高级篇】MyBatis 拦截器

【MybBatis高级篇】MyBatis 拦截器

  • 拦截器介绍
  • 实现拦截器
  • 注册拦截器
  • 应用
    • yml
    • @DynamicSql
    • Dao 层代码
    • xml
    • 启动类
    • 拦截器核心代码
    • 代码测试
  • 拦截器应用场景

MyBatis 是一个流行的 Java 持久层框架,它提供了灵活的 SQL 映射和执行功能。有时候我们可能需要在运行时动态地修改 SQL 语句,例如添加一些条件(创建时间、修改时间)、排序、分页等。MyBatis 提供了一个强大的机制来实现这个需求,那就是拦截器(Interceptor)

拦截器介绍

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

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

实现拦截器

1、定义一个实现 org.apache.ibatis.plugin.Interceptor 接口的拦截器类,并重写其中的 interceptpluginsetProperties 方法。

public interface Interceptor {Object intercept(Invocation var1) throws Throwable;Object plugin(Object var1);void setProperties(Properties var1);
}
  • intercept(Invocation invocation) :从上面我们了解到interceptor能够拦截的四种类型对象,此处入参invocation便是指拦截到的对象。
    举例说明:拦截StatementHandler#query(Statement st,ResultHandler rh) 方法,那么Invocation就是该对象。
  • plugin(Object target) :这个方法的作用是就是让mybatis判断,是否要进行拦截,然后做出决定是否生成一个代理。
  • setProperties(Properties properties) : 拦截器需要一些变量对象,而且这个对象是支持可配置的。

2、添加 @Intercepts 注解,写上需要拦截的对象和方法,以及方法参数,例如 @Intercepts({@Signature(type = StatementHandler.class, method = “prepare”, args = {Connection.class, Integer.class})}),表示在 SQL 执行之前进行拦截处理

3、配置文件中添加拦截器

注册拦截器

1、xml方式

<plugins><plugin interceptor="xxxx.CustomInterceptor"></plugin>
</plugins>

2、mybatis-spring-boot-start方式,只要使用@Component/@Bean把类注册到容器即可

@Component
@Slf4j
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class DynamicSqlInterceptor implements Interceptor {...
}

应用

根据方法是否包含动态切换的注解标识,替换sql中包含的信息

yml

指定 xml 文件中需要替换的占位符标识:@dynamicSql 以及待替换日期条件。

spring:datasource:#   数据源基本配置url: jdbc:mysql://localhost:3306/order_db_1?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=trueusername: rootpassword: rootdriver-class-name: com.mysql.cj.jdbc.Driverinitialization-mode: always #表示始终都要执行初始化,2.x以上版本需要加上这行配置type: com.alibaba.druid.pool.DruidDataSource#   数据源其他配置initialSize: 5minIdle: 5maxActive: 20maxWait: 60000timeBetweenEvictionRunsMillis: 60000minEvictableIdleTimeMillis: 300000validationQuery: SELECT 1 FROM DUALtestWhileIdle: truetestOnBorrow: falsetestOnReturn: falsepoolPreparedStatements: true#   配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙filters: stat,wall,log4jmaxPoolPreparedStatementPerConnectionSize: 20useGlobalDataSourceStat: trueconnectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
mybatis-plus:configuration:# 驼峰转换 从数据库列名到Java属性驼峰命名的类似映射map-underscore-to-camel-case: false# 是否开启缓存cache-enable: false# 如果查询结果中包含空值的列,则 MyBatis 在映射的时候,不会映射这个字段#call-setters-on-nulls: true# 打印sqllog-impl: org.apache.ibatis.logging.stdout.StdOutImplmapper-locations: classpath*:mapper/*.xml# 动态sql配置
dynamicSql:placeholder: "@dynamicSql"date: "2023-07-31"

@DynamicSql

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DynamicSql {
}

Dao 层代码

在需要进行 SQL 占位符替换的方法上加 @DynamicSql 注解。

public interface DynamicSqlMapper  {@DynamicSqlLong count();Long save();
}

xml

<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.zysheep.mapper.DynamicSqlMapper"><select id="count" resultType="java.lang.Long">select count(1) from t_order_1 where create_time > @dynamicSql</select>
</mapper>

启动类

@MapperScan(basePackages = "cn.zysheep.mapper")
@SpringBootApplication
public class DmApplication {public static void main(String[] args) {SpringApplication.run(DmApplication.class, args);}
}

拦截器核心代码

@Component
@Slf4j
@Intercepts({@Signature(type = StatementHandler.class,method = "prepare", args = {Connection.class, Integer.class})
})
public class DynamicSqlInterceptor implements Interceptor {@Value("${dynamicSql.placeholder}")private String placeholder;@Value("${dynamicSql.date}")private  String dynamicDate;@Overridepublic Object intercept(Invocation invocation) throws Throwable {// 1. 获取 StatementHandler 对象也就是执行语句StatementHandler statementHandler = (StatementHandler) invocation.getTarget();// 2. MetaObject 是 MyBatis 提供的一个反射帮助类,可以优雅访问对象的属性,这里是对 statementHandler 对象进行反射处理,MetaObject metaObject = MetaObject.forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY,SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY,new DefaultReflectorFactory());// 3. 通过 metaObject 反射获取 statementHandler 对象的成员变量 mappedStatementMappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");// mappedStatement 对象的 id 方法返回执行的 mapper 方法的全路径名,如cn.zysheep.mapper.DynamicSqlMapper.countString id = mappedStatement.getId();// 4. 通过 id 获取到 Dao 层类的全限定名称,然后反射获取 Class 对象Class<?> classType = Class.forName(id.substring(0, id.lastIndexOf(".")));// 5. 获取包含原始 sql 语句的 BoundSql 对象BoundSql boundSql = statementHandler.getBoundSql();String sql = boundSql.getSql();log.info("替换前---sql:{}", sql);// 拦截方法String mSql = null;// 6. 遍历 Dao 层类的方法for (Method method : classType.getMethods()) {// 7. 判断方法上是否有 DynamicSql 注解,有的话,就认为需要进行 sql 替换if (method.isAnnotationPresent(DynamicSql.class)) {mSql = sql.replaceAll(placeholder, String.format("'%s'", dynamicDate));break;}}if (StringUtils.isNotBlank(mSql)) {log.info("替换后---mSql:{}", mSql);// 8. 对 BoundSql 对象通过反射修改 SQL 语句。Field field = boundSql.getClass().getDeclaredField("sql");field.setAccessible(true);field.set(boundSql, mSql);}// 9. 执行修改后的 SQL 语句。return invocation.proceed();}@Overridepublic Object plugin(Object target) {// 使用 Plugin.wrap 方法生成代理对象return Plugin.wrap(target, this);}@Overridepublic void setProperties(Properties properties) {// 获取配置文件中的属性值}
}

代码测试

@SpringBootTest(classes = DmApplication.class)
public class DynamicTest {@Autowiredprivate DynamicSqlMapper dynamicSqlMapper;@Testpublic void test() {Long count = dynamicSqlMapper.count();Assert.notNull(count, "count不能为null");}
}

在这里插入图片描述

拦截器应用场景

1、SQL 语句执行监控:可以拦截执行的 SQL 方法,打印执行的 SQL 语句、参数等信息,并且还能够记录执行的总耗时,可供后期的 SQL 分析时使用。
2、SQL 分页查询:MyBatis 中使用的 RowBounds 使用的内存分页,在分页前会查询所有符合条件的数据,在数据量大的情况下性能较差。通过拦截器,可以在查询前修改 SQL 语句,提前加上需要的分页参数。
3、公共字段的赋值:在数据库中通常会有 createTime , updateTime 等公共字段,这类字段可以通过拦截统一对参数进行的赋值,从而省去手工通过 set 方法赋值的繁琐过程。
4、数据权限过滤:在很多系统中,不同的用户可能拥有不同的数据访问权限,例如在多租户的系统中,要做到租户间的数据隔离,每个租户只能访问到自己的数据,通过拦截器改写 SQL 语句及参数,能够实现对数据的自动过滤。
5、SQL 语句替换:对 SQL 中条件或者特殊字符进行逻辑替换。(也是本文的应用场景)

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

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

相关文章

基于java线上会议管理系统设计与实现

摘 要 当今日常生活中计算机是普遍存在&#xff0c;而且干涉到各个领域、工作和休闲娱乐中一个不可缺少的一个重要工具。如今,各行各业都在努力推动工业计算机的快速现代化广泛使用,因而提高工作效率以及人们的生活水平。现在的会议逐渐朝着线上举行发展。但也不完全在线上开会…

超细详解,接口自动化测试-JSON和JsonPath提取数据(实战)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 JSON(JavaScript …

机器学习中的 K-均值聚类算法及其优缺点

K-均值聚类算法是一种常用的无监督学习算法&#xff0c;用于将相似的数据点分组为聚类。 其步骤如下&#xff1a; 1. 初始化&#xff1a;选择聚类数K&#xff0c;随机选取K个聚类中心。 2. 计算距离&#xff1a;计算每个数据点与K个聚类中心的距离&#xff0c;将其分配到距离最…

kafka总结

Kafka是一种高吞吐量的分布式发布订阅消息系统&#xff08;消息引擎系统&#xff09;&#xff0c;它可以处理消费者在网站中的所有动作流数据。 消息队列应用场景 缓存/削峰 :处理突然激增的大量数据&#xff0c;先放入消息队列&#xff0c;再按照速度去处理&#xff0c; 解…

Redis中的缓存雪崩、击穿、穿透的原因以及解决办法

redis的缓存 雪崩 击穿1.缓存雪崩双11访问很大,比如说redis设置缓存时间为3小时&#xff0c;当购物超过3小时之后 首页redis 在一瞬间全部失效,导致所有请求都打在db上.造成db在响应不及时直接就挂掉了 这个时候首页就不能立马对外响应服务了redis的key大面积失效 导致前端直接…

一个完整的http请求响应过程

一、 HTTP请求和响应步骤 图片来自&#xff1a;理解Http请求与响应 以上完整表示了HTTP请求和响应的7个步骤&#xff0c;下面从TCP/IP协议模型的角度来理解HTTP请求和响应如何传递的。 二、TCP/IP协议 TCP/IP协议模型&#xff08;Transmission Control Protocol/Internet Pr…

iBATIS之父:iBATIS框架的成功蜕变

作为软件开发人员&#xff0c;我已经在许多不同的环境中工作过。即使是在同一家公司中&#xff0c;软件开发的方式也往往是迥然不同的。开发人员每天都要面对各种各样的挑战、面对不同的人和工具&#xff0c;考虑到这一点&#xff0c;你就会迅速了解他们的世界是多么的变化多端…

设计模式学习之开闭原则

学习内容均来自抖音号 【it楠老师教java】课程。 1、原理概述 开闭原则的英文全称是 Open Closed Principle&#xff0c;简写为 OCP。它的英文描述是&#xff1a;software entities (modules, classes, functions, etc.) should be open for extension , but closed for modi…

windows基础命令

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言 一.目录和文件的操作 1.cd 命令 切换到d盘 2.目录分为相对路径和绝对路径 3. dir命令 用于显示目录和文件列表 4. md 或 mkdir 创建目录 5. rd 用于删…

数据结构——AVL树

文章目录 一.AVL树的定义二.AVL树的插入三.插入后更新平衡因子四.AVL树的旋转1.左单旋2.右单旋3.先左单旋再右单旋4.先右单旋再左单旋 五.检查是否满足AVL树六.源码 一.AVL树的定义 二叉搜索树虽可以缩短查找的效率&#xff0c;但如果数据有序或接近有序二叉搜索树将退化为单支…

智慧水务和物联网智能水表在农村供水工程中的应用

摘 要&#xff1a;随着社会的进步和各项事业的飞速发展&#xff0c;人民生活水平的逐步提升&#xff0c;国家对农村饮水安全有了更高的要求&#xff0c;为了进一步提升农村供水服务的质量&#xff0c;利用现代化、信息化科学技术提升农村供水服务质量&#xff0c;提高用水管理效…

基于高通QCC5171的对讲机音频数据传输系统设计

一 研发资料准备 二 设计方法 蓝牙连接与配对&#xff1a;使用QCC5171的蓝牙功能&#xff0c;实现设备之间的蓝牙连接和配对。确保设备能够相互识别并建立起稳定的蓝牙连接。 音频采集与处理&#xff1a;将麦克风采集到的音频数据通过QCC5171的ADC&#xff08;模数转换器&…

upload-labs详解------持续更新

目录 注&#xff1a; 搭建&#xff1a; pass-01&#xff08;前端绕过&#xff09; pass-02&#xff08;后缀绕过&#xff09; pass-03&#xff08;黑名单绕过&#xff09; pass-04&#xff08;Apache解析漏洞\.htaccess文件绕过&#xff09; 注&#xff1a; 本项目提供的…

Vc - Qt - Qt::KeepAspectRatio及Qt.SmoothTransformation

Qt::KeepAspectRatio是一个枚举值&#xff0c;用于指定图像的缩放行为。设置Qt::KeepAspectRatio属性后&#xff0c;图像将按比例缩放以适应目标矩形&#xff0c;并保持其长宽比。如果目标矩形的宽高比与图像的宽高比不一致&#xff0c;则图像的一部分会被剪裁掉。 Qt::SmoothT…

如祺出行冲刺自动驾驶商业化,人少的地方机会多?

网约车&#xff0c;正在迎来让人“不明觉厉”的新一轮竞赛。 网约车监管信息交互系统的数据显示&#xff0c;截至今年6月30日&#xff0c;全国共有318家网约车平台公司取得网约车平台经营许可&#xff0c;环比增加5家&#xff1b;网约车监管信息交互系统6月份共收到订单信息7.…

记一道有趣的sql题

有一张运单表&#xff1a;dwd_biz_waybill_td&#xff0c;该表的主键是way_bill_id&#xff0c;并且有如下字段&#xff1a; way_bill_id&#xff08;运单表主键&#xff09;&#xff0c;shiping_date&#xff08;下单日期&#xff0c;时间格式为yyyy-MM-dd&#xff09;&#…

爬虫原理详解及requests抓包工具用法介绍

文章目录 一、什么是爬虫&#xff1f;二、爬虫的分类三、网址的构成四、爬虫的基本步骤五、动态页面和静态页面六、伪装请求头七、requests库介绍1. 概念&#xff1a;2. 安装方式&#xff08;使用镜像源&#xff09;&#xff1a;3. 基本使用&#xff1a;4. response对象对应的方…

使用Express部署Vue项目

使用Express部署Vue项目 目录 1. 背景 2. 配置Vue CLI 1.1 安装nodejs 1.2 创建vue-cli 1.3 创建vue项目 1.4 构建vue项目3. 配置Express 2.1 安装express 2.2 创建项目4. 使用express部署vue项目 1&#xff0c;背景 我们想要做一个前后端分离的课程项目&#xff0c;前端…

eclipse版本与jdk版本对应关系

官网&#xff1a;Eclipse/Installation - Eclipsepedia eclipse历史版本&#xff08;2007-&#xff09;&#xff1a;Older Versions Of Eclipse - Eclipsepedia Eclipse Packaging Project (EPP) Releases | Eclipse Packages

ARM裸机-10

1、X210开发板和光盘资料 1.1、配置信息 CPU&#xff1a;三星S5PV210 内存&#xff1a;512M DDR2 SDRAM Flash&#xff1a;4GB iBand LCD&#xff1a;7寸&#xff0c;分辨率800x480 触摸屏&#xff1a;电容触摸屏 2、X210开发板硬件手册 3、X210开发板刷系统 3.1、什么是刷…