MyBatis拦截器使用方法

news2024/10/7 8:26:08

前言

MyBatis拦截器可以做的工作:SQL修改分页操作数据过滤SQL执行时间性能监控等。

1. 基础介绍

1.1. 核心对象

MyBatis代码实现的角度来看,MyBatis的主要的核心部件有以下几个:

        ·Configuration:初始化基础配置,比如MyBatis的别名等,一些重要的类型对象,如插件,映射器,ObjectFactorytypeHandler对象,MyBatis所有的配置信息都维持在Configuration对象之中。
        ·SqlSessionFactorySqlSession工厂。
        ·SqlSession:作为MyBatis工作的主要顶层API,表示和数据库交互的会话,完成必要的数据库增删改查功能。
        ·ExecutorMyBatis的内部执行器,它负责调用StatementHandler操作数据库,并把结果集通过ResultSetHandler进行自动映射,另外,它还处理二级缓存的操作。
        ·StatementHandlerMyBatis直接在数据库执行SQL脚本的对象。另外它也实现了MyBatis的一级缓存。
        ·ParameterHandler:负责将用户传递的参数转换成JDBC Statement所需要的参数。是MyBatis实现SQL入参设置的对象。
        ·ResultSetHandler:负责将JDBC返回的ResultSet结果集对象转换成List类型的集合。是MyBatisResultSet集合映射成POJO的接口对象。
        ·TypeHandler:负责Java数据类型和JDBC数据类型之间的映射和转换。
        ·MappedStatementMappedStatement维护了一条<select|update|delete|insert>节点的封装。
        ·SqlSource :负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回。
        ·BoundSql:表示动态生成的SQL语句以及相应的参数信息。

1.2. 执行过程

2. 实现步骤

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

        2.添加@Intercepts注解,写上需要拦截的对象和方法,以及方法参数。
        3.Spring项目注意添加@Component注解即可,使其成为Spring管理的一个Bean

2.1. 添加注解
MyBatis拦截器默认可以拦截的类型只有四种,即四种接口类型ExecutorStatementHandlerParameterHandlerResultSetHandler。对于我们的自定义拦截器必须使用MyBatis提供的@Intercepts注解来指明我们要拦截的是四种类型中的哪一种接口。

@Signature注解的参数:

@Intercepts({
    @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
    @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class}),
    @Signature(type = ParameterHandler.class, method = "setParameters", args = {PreparedStatement.class}),
    @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})
})
2.1.1. type

MyBatis拦截器默认会按顺序拦截以下的四个接口中的所有方法:

org.apache.ibatis.executor.Executor  //拦截执行器方法
org.apache.ibatis.executor.statement.StatementHandler  //拦截SQL语法构建处理
org.apache.ibatis.executor.parameter.ParameterHandler  //拦截参数处理
org.apache.ibatis.executor.resultset.ResultSetHandler  //拦截结果集处理

具体是拦截这四个接口对应的实现类:

org.apache.ibatis.executor.CachingExecutor
org.apache.ibatis.executor.statement.RoutingStatementHandler
org.apache.ibatis.scripting.defaults.DefaultParameterHandler
org.apache.ibatis.executor.resultset.DefaultResultSetHandler

2.1.2. method

这个可以根据MyBatis源码了解下。

2.1.3. args

根据参数类型区分重载的方法。

2.2. 方法实现
2.2.1. intercept

进行拦截的时候要执行的方法。该方法参数Invocation类中有三个字段:

  private final Object target;
  private final Method method;
  private final Object[] args;

可通过这三个字段分别获取下面的信息:

Object target = invocation.getTarget();//被代理对象
Method method = invocation.getMethod();//代理方法
Object[] args = invocation.getArgs();//方法参数

2.2.2. plugin
插件用于封装目标对象的,通过该方法我们可以返回目标对象本身,也可以返回一个它的代理,可以决定是否要进行拦截进而决定要返回一个什么样的目标对象,官方提供了示例:return Plugin.wrap(target, this);,可以在这个方法中提前进行拦截对象类型判断,提高性能:

    @Override
    public Object plugin(Object target) {
        //只对要拦截的对象生成代理
        if(target instanceof StatementHandler){
            //调用插件
            return Plugin.wrap(target, this);
        }
        return target;
    }

MyBatis拦截器用到责任链模式+动态代理+反射机制;
所有可能被拦截的处理类都会生成一个代理类,如果有N个拦截器,就会有N个代理,层层生成动态代理是比较耗性能的。而且虽然能指定插件拦截的位置,但这个是在执行方法时利用反射动态判断的,初始化的时候就是简单的把拦截器插入到了所有可以拦截的地方。所以尽量不要编写不必要的拦截器。另外我们可以在调用插件的地方添加判断,只要是当前拦截器拦截的对象才进行调用,否则直接返回目标对象本身,这样可以减少反射判断的次数,提高性能。

2.2.3. setProperties
如果我们拦截器需要用到一些变量参数,而且这个参数是支持可配置的,类似Spring中的@Value("${}")从application.properties文件获取自定义变量属性,这个时候我们就可以使用这个方法。

(1)在application.properties文件中添加配置:

mybatis.config-location=classpath:mybatis-config.xml

(2)在resources目录下添加mybatis-config.xml配置文件,并添加插件和属性配置。添加完需要注意去掉自定义MyBatis拦截器上的@Component注解,否则该拦截器相当于注册了两个,会执行两遍拦截方法。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
         "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <plugins>
        <plugin interceptor="com.example.demo.mapper.plugin.MyPlugin">
            <property name="key1" value="value1"/>
            <property name="key2" value="value2"/>
            <property name="key3" value="value3"/>
        </plugin>
    </plugins>
</configuration>

(3)在拦截器插件的setProperties方法中进行。这些自定义属性参数会在项目启动的时候被加载。

    @Override
    public void setProperties(Properties properties) {
        System.out.println("key1=" + properties.getProperty("key1"));
        System.out.println("key2=" + properties.getProperty("key2"));
        System.out.println("key3=" + properties.getProperty("key3"));
    }

3. 代码示例

package com.example.demo.mapper.plugin;

import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.*;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.util.Properties;

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

    Properties properties = null;

    /**
     * 拦截方法逻辑
     * 这里主要是通过反射去获取要执行的SQL相关信息,然后进行操作
     */
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object target = invocation.getTarget();//被代理对象
        Method method = invocation.getMethod();//代理方法
        Object[] args = invocation.getArgs();//方法参数
        // do something ...... 方法拦截前执行代码块
        Object result = invocation.proceed();
        // do something .......方法拦截后执行代码块
        return result;
    }

    /**
     * 生成MyBatis拦截器代理对象
     */
    @Override
    public Object plugin(Object target) {
        if(target instanceof StatementHandler){
            //调用插件
            return Plugin.wrap(target, this);
        }
        return target;
    }

    /**
     * 设置插件属性(直接通过Spring的方式获取属性,所以这个方法一般也用不到)
     * 项目启动的时候数据就会被加载
     */
    @Override
    public void setProperties(Properties properties) {
        //赋值成员变量,在其他方法使用。
        this.properties = properties;
    }
}

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

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

相关文章

InfiniGate自研网关实现思路七

25.网关Nginx负载模型配置 通过模拟多个HTTP服务配置到 Nginx 做负载均衡&#xff0c;以学习API网关负载的配置和使用 API 网关是用于支撑分布式 RPC 接口协议转换提供 HTTP 调用的一套服务&#xff0c;那么 API 网关系统就需要可横向扩展来满足系统的吞吐量诉求。所以这里需…

如何根据业务需求,轻松挑选SSL证书?

在当今数字化时代&#xff0c;网站的安全性愈发受到重视。SSL证书作为网站安全的“守门员”&#xff0c;不仅能保护用户数据不被窃取&#xff0c;还能提升网站的信任度。但面对市场上琳琅满目的SSL证书产品&#xff0c;如何根据业务需求挑选合适的证书呢&#xff1f;今天&#…

今日份动态规划学习

主要只搞了一个这道题&#xff0c;有点摸鱼了今天晚上&#xff0c;也是来小看一下这道题吧01背包完全背包 P1941 [NOIP2014 提高组] 飞扬的小鸟 题意&#xff1a; 这题是说&#xff0c;给我们一个游戏界面&#xff0c;界面的长度为n&#xff08;水平距离&#xff09;&#x…

Java | Leetcode Java题解之第134题加油站

题目&#xff1a; 题解&#xff1a; class Solution {public int canCompleteCircuit(int[] gas, int[] cost) {int n gas.length;int i 0;while (i < n) {int sumOfGas 0, sumOfCost 0;int cnt 0;while (cnt < n) {int j (i cnt) % n;sumOfGas gas[j];sumOfCos…

【活动】程序员的核心职业素养:技术与人文并重的探索之旅

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 程序员的核心职业素养&#xff1a;技术与人文并重的探索之旅1. 持续学习与技术精…

Python Lambda函数的应用实例教程

在Python编程中&#xff0c;lambda函数是一种简洁且强大的工具&#xff0c;用于创建小型匿名函数。它们在需要快速定义简单函数时特别有用。本文将详细介绍lambda函数的语法及其多种应用实例&#xff0c;帮助读者更好地理解和使用lambda函数。 一、lambda函数的基本概念 1.1 什…

多个p标签一行展示,溢出隐藏

一开始&#xff0c;我是让div包裹多个p标签&#xff0c;并让div“flex”布局&#xff0c;且单行溢出隐藏&#xff0c;可是发现当父元素或当前元素有flex时&#xff0c;text-overflow: ellipsis;是不生效的 大多数解决办法都是&#xff0c;不要flex&#xff0c;或者给div下的每个…

计算机网络 期末复习(谢希仁版本)第6章

DNS采用UDP。 DHCP 给运行服务器软件、且位置固定的计算机指派一个永久地址&#xff0c;给运行客户端软件的计算机分配一个临时地址

数据库(25)——多表关系介绍

在项目开发中&#xff0c;进行数据库表结构设计时&#xff0c;会根据业务需求及业务模块之间的关系&#xff0c;分析并设计表结构&#xff0c;各个表之间的结构基本上分为三种&#xff1a;一对多&#xff0c;多对多&#xff0c;一对一。 一对多 例如&#xff0c;一个学校可以有…

Web IDE 在线编辑器综合实践(Web IDE 技术探索 三)

前言 前面两篇文章&#xff0c;我们简单讲述了 WebContainer/api 、Terminal 的基本使用&#xff0c;离完备的在线代码编辑器就差一个代码编辑了。今天通过 monaco editor &#xff0c;来实现初级代码编辑功能&#xff0c;讲述的是整个应用的搭建&#xff0c;并不单独针对monac…

鸿蒙轻内核M核源码分析系列六 任务及任务调度(2)任务模块

任务是操作系统一个重要的概念&#xff0c;是竞争系统资源的最小运行单元。任务可以使用或等待CPU、使用内存空间等系统资源&#xff0c;并独立于其它任务运行。鸿蒙轻内核的任务模块可以给用户提供多个任务&#xff0c;实现任务间的切换&#xff0c;帮助用户管理业务程序流程。…

设计模式-工厂方法(创建型)

创建型-工厂方法 简单工厂 将被创建的对象称为“产品”&#xff0c;将生产“产品”对象称为“工厂”&#xff1b;如果创建的产品不多&#xff0c;且不需要生产新的产品&#xff0c;那么只需要一个工厂就可以&#xff0c;这种模式叫做“简单工厂”&#xff0c;它不属于23中设计…

【PL理论】(5) F#:递归类型 | Immutability 特性(F#中值一旦定义就不会改变)

&#x1f4ad; 写在前面&#xff1a;本文旨在探讨不可变数据结构在 F# 编程中的应用&#xff0c;特别是如何利用递归记录类型来表示和操作数值表达式。通过定义存储整数的二叉树和数值表达式的类型&#xff0c;我们将展示不可变性如何简化程序的理解和维护。文章将对比 F# 与命…

LeetCode刷题之HOT100之搜索旋转排序数组

2024/6/2 雨一直下&#xff0c;一个上午都在床上趴着看完了《百年孤独》&#xff0c;撑伞去吃了个饭&#xff0c;又回到了宿舍。打开许久未开的老电脑&#xff0c;准备做题了。《百年孤独》讲了什么&#xff0c;想表达什么&#xff0c;想给读者留下什么&#xff0c;我不知道&am…

IP黑名单与IP白名单是什么?

在IP代理使用中&#xff0c;我们经常听到黑名单与白名单两个名词&#xff0c;它们不仅提供了强大的防御机制&#xff0c;还可以灵活应对不同的安全威胁。本文将详细探讨IP黑名单和白名单在网络安全中的双重屏障作用。 一、IP黑名单和白名单定义 IP黑名单与IP白名单是网络安全中…

2024会声会影全新旗舰版,下载体验!

在当今数字时代&#xff0c;视频内容已成为最受欢迎的媒介之一。无论是个人娱乐、教育还是商业推广&#xff0c;优秀的视频制作都是吸引观众的关键。为了满足广大用户对高质量视频制作软件的需求&#xff0c;我们隆重推出了会声会影2024最新旗舰版。这款软件不仅集成了最先进的…

六、SQL执行器的定义和实现

之前的Sql执行都是耦合在SqlSession里的&#xff0c;现在要对这部分进行解耦和重构&#xff0c;引申出执行器&#xff0c;查了相关概念&#xff0c;Executor执行器可以说是定义了一个个的SQL的执行流程&#xff0c;用查询方法举例&#xff0c;大概一下几步&#xff1a; 1.获取数…

基于SOA海鸥优化算法的三维曲面最高点搜索matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 基于SOA海鸥优化算法的三维曲面最高点搜索matlab仿真&#xff0c;输出收敛曲线以及三维曲面最高点搜索结果。 2.测试软件版本以及运行结果展示 MATLAB2022A版本…

OPPO 文件传输 - 将文件从 OPPO 手机传输到 PC 的 5 种方法

OPPO手机以其出色的拍照功能而闻名&#xff0c;尤其是新推出的OPPO Find X2系列&#xff0c;它配备了高清前置镜头和超夜景模式&#xff0c;让您轻松拍出精彩瞬间。当您需要将这些照片或其他文件从OPPO手机传输到PC时&#xff0c;以下是五种简便的方法。 第 1 部分&#xff…

掘金AI商战宝典-高阶班:如何用AI制作视频(11节视频课)

课程下载&#xff1a;掘金AI商战宝典-高阶班&#xff1a;如何用AI制作视频(11节视频课)-课程网盘链接提取码下载.txt资源-CSDN文库 更多资源下载&#xff1a;关注我。 课程目录&#xff1a; 1-第一讲用AI自动做视频(上)_1.mp4 2-第二讲用AI自动做视频(中)_1.mp4 3-第四讲A…