• Mybatis 拦截器 说明和使用 (二)


    一.处理器类简介

    1.主要类梳理

    作用
    org.apache.ibatis.session.Configuration数据源配置信息、插件、映射器等
    org.apache.ibatis.session.SqlSession顶层API,数据库会话类
    org.apache.ibatis.session.SqlSessionFactorySqlSession工厂
    org.apache.ibatis.type.TypeHandlerJdbcType 和 JavaType 映射
    org.apache.ibatis.mapping.BoundSql动态生成 SQL 的封装类
    org.apache.ibatis.mapping.MappedStatementxml 标签配置信息封装对象

    2.处理器接口

    处理器接口默认实现方法
    ParameterHandlerDefaultParameterHandlergetParameterObject()
    setParameters(PreparedStatement ps)
    ResultSetHandlerDefaultResultSetHandlerhandleResultSets(Statement stmt)
    handleCursorResultSets(Statement stmt)
    handleOutputParameters(CallableStatement cs)
    StatementHandlerBaseStatementHandler
    CallableStatementHandler
    PreparedStatementHandler
    RoutingStatementHandler
    SimpleStatementHandler
    prepare(Connection connection, Integer transactionTimeout)
    parameterize(Statement statement)
    batch(Statement statement)
    update(Statement statement)
    query(Statement statement, ResultHandler resultHandler)
    queryCursor(Statement statement)
    getBoundSql()
    getParameterHandler()
    ExecutorBaseExecutor
    BatchExecutor
    CachingExecutor
    ClosedExecutor
    ReuseExecutor
    SimpleExecutor

    update(MappedStatement ms, Object parameter)
    query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql)
    query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler)
    queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds)
    flushStatements()
    commit(boolean required)
    rollback(boolean required)
    createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql)
    isCached(MappedStatement ms, CacheKey key)
    clearLocalCache()
    deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class targetType)
    getTransaction()
    close(boolean forceRollback)
    isClosed()
    setExecutorWrapper(Executor executor)

    3.简要调用流程(仅做参考,不标准)

    在这里插入图片描述

    SimpleExecutor -> newStatementHandler -> RoutingStatementHandler -> SimpleStatementHandler -> newParameterHandler -> newResultSetHandler
    
    • 1

    mapper 注入后,在运行时,是一个代理对象

    在这里插入图片描述

    二.拦截器应用

    1.环境准备

    在这里插入图片描述

    1.依赖包(多环境打包可忽略)

    
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0modelVersion>
    
        <groupId>org.examplegroupId>
        <artifactId>demoartifactId>
        <version>1.0-SNAPSHOTversion>
    
        <properties>
            <maven.compiler.source>8maven.compiler.source>
            <maven.compiler.target>8maven.compiler.target>
            <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
        properties>
    
        
        <repositories>
            <repository>
                <id>aliyunid>
                <url>https://maven.aliyun.com/repository/publicurl>
                <releases>
                    <enabled>trueenabled>
                releases>
                <snapshots>
                    <enabled>falseenabled>
                snapshots>
            repository>
        repositories>
        <pluginRepositories>
            <pluginRepository>
                <id>aliyun-pluginid>
                <url>https://maven.aliyun.com/nexus/content/groups/public/url>
                <snapshots>
                    <enabled>falseenabled>
                snapshots>
            pluginRepository>
        pluginRepositories>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-webartifactId>
                <version>2.7.4version>
            dependency>
    
            <dependency>
                <groupId>org.projectlombokgroupId>
                <artifactId>lombokartifactId>
                <version>1.18.24version>
            dependency>
    
            <dependency>
                <groupId>com.alibabagroupId>
                <artifactId>druid-spring-boot-starterartifactId>
                <version>1.2.11version>
                <exclusions>
                    <exclusion>
                        <groupId>org.springframework.bootgroupId>
                        <artifactId>spring-boot-autoconfigureartifactId>
                    exclusion>
                exclusions>
            dependency>
            <dependency>
                <groupId>org.postgresqlgroupId>
                <artifactId>postgresqlartifactId>
                <version>42.5.0version>
            dependency>
    
            <dependency>
                <groupId>org.mybatis.spring.bootgroupId>
                <artifactId>mybatis-spring-boot-starterartifactId>
                <version>2.2.2version>
            dependency>
        dependencies>
    
    
        
        <profiles>
            <profile>
                <id>testid>
                <properties>
                    <properties.active>testproperties.active>
                properties>
                <activation>
                    <activeByDefault>trueactiveByDefault>
                activation>
            profile>
            <profile>
                <id>devid>
                <properties>
                    <properties.active>devproperties.active>
                properties>
            profile>
            <profile>
                <id>prodid>
                <properties>
                    <properties.active>prodproperties.active>
                properties>
            profile>
        profiles>
    
        <build>
            <finalName>demofinalName>
            <plugins>
                <plugin>
                    <groupId>org.springframework.bootgroupId>
                    <artifactId>spring-boot-maven-pluginartifactId>
                    <version>2.7.4version>
                    <executions>
                        <execution>
                            <goals>
                                <goal>repackagegoal>
                            goals>
                        execution>
                    executions>
                plugin>
            plugins>
            <resources>
                
                <resource>
                    <directory>src/main/resourcesdirectory>
                    <excludes>
                        <exclude>application*.ymlexclude>
                    excludes>
                resource>
                
                <resource>
                    <directory>src/main/resourcesdirectory>
                    
                    <filtering>truefiltering>
                    
                    <includes>
                        <include>application.ymlinclude>
                        <include>application-${properties.active}.ymlinclude>
                    includes>
                resource>
            resources>
        build>
    
    
    project>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142

    2.配置文件(访问的本地 Postgres 数据库)

    server:
      port: 8093
    
    spring:
      application:
        name: moon
      datasource:
        druid:
          username: postgres
          password: 
          url: jdbc:postgresql://127.0.0.1:5432/wiki_animal_db
          driverClassName: org.postgresql.Driver
          type: com.alibaba.druid.pool.DruidDataSource
          # 初始化大小,最小,最大
          initial-size: 5
          min-idle: 5
          max-active: 20
          # 配置获取连接等待超时的时间
          max-wait: 60000
          # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
          time-between-eviction-runs-millis: 60000
          # 配置一个连接在池中最小生存的时间,单位是毫秒
          min-evictable-idle-time-millis: 300000
          validation-query: select version()
          test-while-idle: true
          test-on-borrow: false
          test-on-return: false
          # 打开PSCache,并且指定每个连接上PSCache的大小
          pool-prepared-statements: true
          #   配置监控统计拦截的filters,去掉后监控界面sql无法统计,wall用于防火墙
          max-pool-prepared-statement-per-connection-size: 20
          filters: stat,wall
          use-global-data-source-stat: true
          # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
          connect-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
    
    #mybatis配置
    mybatis:
      mapper-locations: classpath:/mapper/*.xml  #修改为对应的mapper文件路径
      #驼峰命名
      configuration:
        map-underscore-to-camel-case: true
    
    #打印sql
    logging:
      level:
        com.demo: info
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    3.启动类

    package com.demo;
    
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    /**
     * @author
     * @date 2022-10-13 19:22
     * @since 1.8
     */
    @MapperScan("com.demo.mapper")
    @SpringBootApplication
    public class DemoApplication {
    
        /**
         * 主类
         * @param args
         */
        public static void main(String[] args) {
            SpringApplication.run(DemoApplication.class,args);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    4.数据库连接池配置

    package com.demo.confin;
    
    import com.alibaba.druid.pool.DruidDataSource;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Primary;
    import org.springframework.stereotype.Component;
    
    import java.sql.SQLException;
    import java.util.Properties;
    
    /**
     * @author 
     * @date 2022-10-14 16:29
     * @since 1.8
     */
    @Slf4j
    @Component
    public class AnimalDataSource {
    
        /**
         * 获取配置
         * @return
         */
        @Bean(name = "druidProperties")
        @ConfigurationProperties(prefix = "spring.datasource")
        public Properties druidProperties(){
            return new Properties();
        }
    
        /**
         * @description: postgres dataSource
         * @params: [properties]
         * @return: com.alibaba.druid.pool.DruidDataSource
         * @create: 2022-08-18
         */
        @Primary
        @Bean(name = "druidDataSource")
        public DruidDataSource druidDataSource(@Qualifier("druidProperties")Properties properties){
            DruidDataSource druidDataSource = new DruidDataSource();
            druidDataSource.configFromPropety(properties);
            try {
                druidDataSource.init();
            } catch (SQLException e) {
                log.error("Animal Datasource Init Exception:",e);
            }
            return druidDataSource;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    5.MVC 层代码

    package com.demo.controller;
    
    import com.demo.entity.AnimalEntity;
    import com.demo.service.AnimalService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    /**
     * @author 
     * @date 2022-10-17 19:48
     * @since 1.8
     */
    @RestController
    @RequestMapping("/animal")
    public class AnimalController {
    
    
        @Autowired
        AnimalService animalService;
    
        @GetMapping("/query")
        public List<AnimalEntity>query() {
            return animalService.query();
        }
    
        @GetMapping("/queryByName/{name}")
        public Map<Integer, String> query(@PathVariable("name") String name) {
            return animalService.queryByName(name);
        }
    
        @GetMapping("/queryByCountryAndName")
        public AnimalEntity queryByCountryAndName(String name,String country) {
            return animalService.queryByCountryAndName(name,country);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    package com.demo.service;
    
    import com.demo.entity.AnimalEntity;
    import java.util.List;
    import java.util.Map;
    
    /**
     * @author
     * @date 2022-10-17 19:48
     * @since 1.8
     */
    public interface AnimalService {
    
        /**
         * 查询
         * @return
         */
        List<AnimalEntity> query();
    
        /**
         * 按名字查询
         * @param name
         * @return
         */
        Map<Integer, String> queryByName(String name);
    
        /**
         * 按国家和名字查询
         * @param name
         * @param country
         * @return
         */
        AnimalEntity queryByCountryAndName(String name,String country);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    package com.demo.service.impl;
    
    import com.demo.entity.AnimalEntity;
    import com.demo.mapper.AnimalMapper;
    import com.demo.service.AnimalService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import java.util.List;
    import java.util.Map;
    
    /**
     * @author 
     * @date 2022-10-17 19:50
     * @since 1.8
     */
    @Service
    public class AnimalServiceImpl implements AnimalService {
    
    
        @Autowired
        AnimalMapper animalMapper;
    
        @Override
        public List<AnimalEntity> query() {
            return animalMapper.query();
        }
    
    
        @Override
        public Map<Integer, String> queryByName(String name) {
            return animalMapper.queryByName(name).get(0);
        }
    
        @Override
        public  AnimalEntity queryByCountryAndName(String name,String country){
            return animalMapper.queryByCountryAndName(name,country);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    6.Map 接口和实现

    package com.demo.mapper;
    
    import com.demo.entity.AnimalEntity;
    
    import java.util.List;
    import java.util.Map;
    
    /**
     * @author
     * @date 2022-10-17 19:50
     * @since 1.8
     */
    public interface AnimalMapper {
    
        /**
         * 查询
         * @return
         */
        List<AnimalEntity> query();
    
        /**
         * 查名字
         * @param name
         * @return
         */
        List<Map<Integer, String>> queryByName(String name);
    
        /**
         * 按国家和名字
         * @param name
         * @param country
         * @return
         */
        AnimalEntity queryByCountryAndName(String name,String country);
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    
    DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
    <mapper namespace="com.demo.mapper.AnimalMapper">
    
        <resultMap id="rabbit" type="com.demo.entity.AnimalEntity">
            <result column="id" property="id"/>
            <result column="animal_name" property="name"/>
            <result column="country" property="country"/>
        resultMap>
    
        <select id="query" resultMap="rabbit">
            select id,animal_name,country from public.tb_rabbit_basic
        select>
    
        <select id="queryByName" resultMap="rabbit">
            select id,animal_name,country from public.tb_rabbit_basic where animal_name like '%'||#{name}||'%'
        select>
    
        <select id="queryByCountryAndName" resultMap="rabbit">
            select id,animal_name,country from public.tb_rabbit_basic where animal_name = #{name} and country = #{country}
        select>
    
    mapper>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    7.表结构和数据

    -- public.tb_rabbit_basic definition
    
    -- Drop table
    
    -- DROP TABLE public.tb_rabbit_basic;
    
    CREATE TABLE public.tb_rabbit_basic (
    	id int4 NULL,
    	animal_name varchar NULL,
    	country varchar NULL
    );
    
    INSERT INTO public.tb_rabbit_basic (id,animal_name,country) VALUES
    	 (1,'安哥拉兔子','安哥拉'),
    	 (2,'道奇兔','荷兰'),
    	 (3,'多瓦夫兔','德国'),
    	 (4,'巨型花明兔','比利时');
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    2.拦截器配置方式

    1.Spring注解

    定义拦截器类,在 Executor 阶段拦截了 query 方法

    package com.demo.confin;
    
    import lombok.extern.slf4j.Slf4j;
    import org.apache.ibatis.executor.Executor;
    import org.apache.ibatis.mapping.MappedStatement;
    import org.apache.ibatis.plugin.Interceptor;
    import org.apache.ibatis.plugin.Intercepts;
    import org.apache.ibatis.plugin.Invocation;
    import org.apache.ibatis.plugin.Signature;
    import org.apache.ibatis.session.ResultHandler;
    import org.apache.ibatis.session.RowBounds;
    import org.springframework.stereotype.Component;
    
    import java.util.Properties;
    
    /**
     * @author
     * @date 2022-10-21 23:09
     * @since 1.8
     */
    @Slf4j
    @Component
    @Intercepts({
            @Signature(method = "query",type = Executor.class,args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
    })
    public class AnimalInterceptor implements Interceptor {
    
        @Override
        public Object intercept(Invocation invocation) throws Throwable {
    
            MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
    
            log.info("进入拦截器 Mapper.xml Method ID Is:{}",mappedStatement.getId());
    
            return invocation.proceed();
        }
    
        @Override
        public void setProperties(Properties properties) {
            Interceptor.super.setProperties(properties);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    启动服务并调用

    在这里插入图片描述

    日志:

    2022-10-21 23:14:25.008 DEBUG 15240 --- [           main] com.demo.DemoApplication                 : Running with Spring Boot v2.7.4, Spring v5.3.23
    2022-10-21 23:14:25.009  INFO 15240 --- [           main] com.demo.DemoApplication                 : The following 1 profile is active: "dev"
    2022-10-21 23:14:26.297  INFO 15240 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8093 (http)
    2022-10-21 23:14:26.308  INFO 15240 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
    2022-10-21 23:14:26.308  INFO 15240 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.65]
    2022-10-21 23:14:26.480  INFO 15240 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
    2022-10-21 23:14:26.481  INFO 15240 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1420 ms
    2022-10-21 23:14:26.764  INFO 15240 --- [           main] com.alibaba.druid.pool.DruidDataSource   : {dataSource-1} inited
    2022-10-21 23:14:27.298  INFO 15240 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8093 (http) with context path ''
    2022-10-21 23:14:27.308  INFO 15240 --- [           main] com.demo.DemoApplication                 : Started DemoApplication in 2.796 seconds (JVM running for 3.222)
    2022-10-21 23:14:38.102  INFO 15240 --- [nio-8093-exec-2] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
    2022-10-21 23:14:38.103  INFO 15240 --- [nio-8093-exec-2] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
    2022-10-21 23:14:38.103  INFO 15240 --- [nio-8093-exec-2] o.s.web.servlet.DispatcherServlet        : Completed initialization in 0 ms
    2022-10-21 23:14:38.141  INFO 15240 --- [nio-8093-exec-2] com.demo.confin.AnimalInterceptor        : 进入拦截器 Mapper.xml Method ID Is:com.demo.mapper.AnimalMapper.query
    2022-10-21 23:14:38.581 DEBUG 15240 --- [nio-8093-exec-2] com.demo.mapper.AnimalMapper.query       : ==>  Preparing: select id,animal_name,country from public.tb_rabbit_basic
    2022-10-21 23:14:38.712 DEBUG 15240 --- [nio-8093-exec-2] com.demo.mapper.AnimalMapper.query       : ==> Parameters: 
    2022-10-21 23:14:38.728 DEBUG 15240 --- [nio-8093-exec-2] com.demo.mapper.AnimalMapper.query       : <==      Total: 4
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    2.手动注册

    取消上面自定义拦截器的初始化注解:@Component;并通过程序手动注册,如果多拦截器的情况下,可以取消所有自动注入,并统一手动处理,PageHelper 分页插件失效或者自定义拦截器不生效,多数情况即为注入顺序导致功能相互覆盖引起的

    新增手动注入拦截器类

    package com.demo.confin;
    
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.ApplicationListener;
    import org.springframework.context.event.ContextRefreshedEvent;
    import org.springframework.stereotype.Component;
    
    import java.util.List;
    
    /**
     * @author 
     * @date 2022-10-21 23:19
     * @since 1.8
     */
    @Component
    public class MybatisInterceptorListener implements ApplicationListener<ContextRefreshedEvent> {
    
        @Autowired
        private List<SqlSessionFactory> sqlSessionFactoryList;
    
        /**
         * 控制注册自定义拦截器顺序
         * 1.避免被PageHelper覆盖(暂不涉及)
         * 2.避免循环依赖
         * @param event the event to respond to
         */
        @Override
        public void onApplicationEvent(ContextRefreshedEvent event) {
            for (SqlSessionFactory factory : sqlSessionFactoryList) {
                factory.getConfiguration().addInterceptor(new AnimalInterceptor());
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    重启服务并调用接口
    在这里插入图片描述
    拦截器生效
    在这里插入图片描述

    3.顺序问题

    同一类型的拦截类,处理时才会有顺序问题,Mybatis 四种类型直接的顺序关系是固定的,也即,如果多个拦截器都是拦截 Executor 的同一 query 方法,才会有注册覆盖现象;不会出现Executor 的拦截覆盖了 StatementHandler 的拦截,但是在应用中还是要谨慎注意处理时是否受到"顺序"影响,或者其他处理冲突

    实际观察一下这四个类的处理顺序:Executor、ParameterHandler、StatementHandler、ResultSetHandler

    分别定义四个拦截器,并乱序注入

    package com.demo.confin;
    
    import lombok.extern.slf4j.Slf4j;
    import org.apache.ibatis.executor.Executor;
    import org.apache.ibatis.mapping.MappedStatement;
    import org.apache.ibatis.plugin.Interceptor;
    import org.apache.ibatis.plugin.Intercepts;
    import org.apache.ibatis.plugin.Invocation;
    import org.apache.ibatis.plugin.Signature;
    import org.apache.ibatis.session.ResultHandler;
    import org.apache.ibatis.session.RowBounds;
    
    import java.util.Properties;
    
    /**
     * @author 
     * @date 2022-10-21 23:35
     * @since 1.8
     */
    @Slf4j
    @Intercepts({
            @Signature(method = "query",type = Executor.class,args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
    })
    public class OneInterceptor implements Interceptor {
    
        /**
         *
         * @param invocation
         * @return
         * @throws Throwable
         */
        @Override
        public Object intercept(Invocation invocation) throws Throwable {
    
            log.info("Proxy {} Target {}",this.getClass().getName(),invocation.getTarget().getClass());
            return invocation.proceed();
        }
    
        @Override
        public void setProperties(Properties properties) {
            Interceptor.super.setProperties(properties);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    package com.demo.confin;
    
    import lombok.extern.slf4j.Slf4j;
    import org.apache.ibatis.executor.parameter.ParameterHandler;
    import org.apache.ibatis.plugin.Interceptor;
    import org.apache.ibatis.plugin.Intercepts;
    import org.apache.ibatis.plugin.Invocation;
    import org.apache.ibatis.plugin.Signature;
    
    import java.sql.PreparedStatement;
    import java.util.Properties;
    
    /**
     * @author 
     * @date 2022-10-21 23:13
     * @since 1.8
     */
    @Slf4j
    @Intercepts({
            @Signature(method = "setParameters",type = ParameterHandler.class,args = {PreparedStatement.class})
    })
    public class TwoInterceptor implements Interceptor {
    
        @Override
        public Object intercept(Invocation invocation) throws Throwable {
    
            log.info("Proxy {} Target {}",this.getClass().getName(),invocation.getTarget().getClass());
    
            return invocation.proceed();
        }
    
        @Override
        public void setProperties(Properties properties) {
            Interceptor.super.setProperties(properties);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    package com.demo.confin;
    
    import lombok.extern.slf4j.Slf4j;
    import org.apache.ibatis.executor.statement.StatementHandler;
    import org.apache.ibatis.plugin.Interceptor;
    import org.apache.ibatis.plugin.Intercepts;
    import org.apache.ibatis.plugin.Invocation;
    import org.apache.ibatis.plugin.Signature;
    import org.apache.ibatis.session.ResultHandler;
    
    import java.sql.Statement;
    import java.util.Properties;
    
    /**
     * @author 
     * @date 2022-10-21 23:53
     * @since 1.8
     */
    @Slf4j
    @Intercepts({
            @Signature(method = "query", type = StatementHandler.class, args = {Statement.class, ResultHandler.class})
    })
    public class ThreeInterceptor implements Interceptor {
    
        /**
         * mapper.xml 不可以重载,会取包加方法名作为
         * @param invocation
         * @return
         * @throws Throwable
         */
        @Override
        public Object intercept(Invocation invocation) throws Throwable {
    
            log.info("Proxy {} Target {}",this.getClass().getName(),invocation.getTarget().getClass());
            return invocation.proceed();
        }
    
        @Override
        public void setProperties(Properties properties) {
            Interceptor.super.setProperties(properties);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    package com.demo.confin;
    
    import lombok.extern.slf4j.Slf4j;
    import org.apache.ibatis.executor.resultset.ResultSetHandler;
    import org.apache.ibatis.plugin.Interceptor;
    import org.apache.ibatis.plugin.Intercepts;
    import org.apache.ibatis.plugin.Invocation;
    import org.apache.ibatis.plugin.Signature;
    
    import java.sql.Statement;
    import java.util.Properties;
    
    /**
     * @author 
     * @date 2022-10-21 23:53
     * @since 1.8
     */
    @Slf4j
    @Intercepts({
            @Signature(method = "handleResultSets", type = ResultSetHandler.class,args = {Statement.class})
    })
    public class FourInterceptor implements Interceptor {
    
        @Override
        public Object intercept(Invocation invocation) throws Throwable {
    
            log.info("Proxy {} Target {}",this.getClass().getName(),invocation.getTarget().getClass());
    
            Object result = invocation.proceed();
    
            //TODO 对结果对象进行加工
            return result;
        }
    
        @Override
        public void setProperties(Properties properties) {
            Interceptor.super.setProperties(properties);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    注册顺序:Executor、ResultSetHandler、ParameterHandler、StatementHandler

    package com.demo.confin;
    
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.ApplicationListener;
    import org.springframework.context.event.ContextRefreshedEvent;
    import org.springframework.stereotype.Component;
    
    import java.util.List;
    
    /**
     * @author
     * @date 2022-10-21 23:19
     * @since 1.8
     */
    @Component
    public class MybatisInterceptorListener implements ApplicationListener<ContextRefreshedEvent> {
    
        @Autowired
        private List<SqlSessionFactory> sqlSessionFactoryList;
    
        /**
         * @param event the event to respond to
         */
        @Override
        public void onApplicationEvent(ContextRefreshedEvent event) {
            for (SqlSessionFactory factory : sqlSessionFactoryList) {
                factory.getConfiguration().addInterceptor(new OneInterceptor());
                factory.getConfiguration().addInterceptor(new FourInterceptor());
                factory.getConfiguration().addInterceptor(new TwoInterceptor());
                factory.getConfiguration().addInterceptor(new ThreeInterceptor());
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    启动服务并调用接口,查看打印结果:127.0.0.1:8093/animal/query

    是按1,2,3,4调用的:
    在这里插入图片描述
    日志内打印了被代理类:CachingExecutor、DefaultParameterHandler、RoutingStatementHandler、DefaultResultSetHandler

    2022-10-21 23:36:33.909  INFO 26032 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8093 (http) with context path ''
    2022-10-21 23:36:33.924  INFO 26032 --- [           main] com.demo.DemoApplication                 : Started DemoApplication in 2.657 seconds (JVM running for 3.071)
    2022-10-21 23:36:41.437  INFO 26032 --- [nio-8093-exec-2] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
    2022-10-21 23:36:41.437  INFO 26032 --- [nio-8093-exec-2] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
    2022-10-21 23:36:41.437  INFO 26032 --- [nio-8093-exec-2] o.s.web.servlet.DispatcherServlet        : Completed initialization in 0 ms
    2022-10-21 23:36:41.475  INFO 26032 --- [nio-8093-exec-2] com.demo.confin.OneInterceptor           : Proxy com.demo.confin.OneInterceptor Target class org.apache.ibatis.executor.CachingExecutor
    2022-10-21 23:36:41.930 DEBUG 26032 --- [nio-8093-exec-2] com.demo.mapper.AnimalMapper.query       : ==>  Preparing: select id,animal_name,country from public.tb_rabbit_basic
    2022-10-21 23:36:42.070  INFO 26032 --- [nio-8093-exec-2] com.demo.confin.TwoInterceptor           : Proxy com.demo.confin.TwoInterceptor Target class org.apache.ibatis.scripting.defaults.DefaultParameterHandler
    2022-10-21 23:36:42.071  INFO 26032 --- [nio-8093-exec-2] com.demo.confin.ThreeInterceptor         : Proxy com.demo.confin.ThreeInterceptor Target class org.apache.ibatis.executor.statement.RoutingStatementHandler
    2022-10-21 23:36:42.071 DEBUG 26032 --- [nio-8093-exec-2] com.demo.mapper.AnimalMapper.query       : ==> Parameters: 
    2022-10-21 23:36:42.075  INFO 26032 --- [nio-8093-exec-2] com.demo.confin.FourInterceptor          : Proxy com.demo.confin.FourInterceptor Target class org.apache.ibatis.executor.resultset.DefaultResultSetHandler
    2022-10-21 23:36:42.088 DEBUG 26032 --- [nio-8093-exec-2] com.demo.mapper.AnimalMapper.query       : <==      Total: 4
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    3.拦截器处理Map

    从上面 Executor 类的 query 方法我们可以看到一个 ResultHandler 结果处理类,实现此接口就能自定义数据处理方式,我们尝试一下将查询结果转为 Map 处理

    自定义Handler,就是取出所需属性值填充到Map,再用List封装一下

    package com.demo.confin.result;
    
    import com.demo.entity.AnimalEntity;
    import org.apache.ibatis.session.ResultContext;
    import org.apache.ibatis.session.ResultHandler;
    
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    /**
     * @author 
     * @date 2022-10-21 23:24
     * @since 1.8
     */
    public class MapResultHandler implements ResultHandler<AnimalEntity> {
    
        /**
         * 定义一个结果:因为如果返回数据为多条,DefaultSqlSession 会自动按List处理,避免报错需要包装一下
         */
        List<Map<Integer, String>> list = new ArrayList<>(1);
    
        /**
         * 初始化MAP
         */
        Map<Integer, String> map = new HashMap<>(16);
    
        @Override
        public void handleResult(ResultContext<? extends AnimalEntity> resultContext) {
            map.put(resultContext.getResultObject().getId(),resultContext.getResultObject().getName());
        }
    
        public Object getResult(){
            list.add(map);
            return list;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    改造一下 OneInterceptor 仅对指定查询处理,并取消其他三个拦截器的注册

    package com.demo.confin;
    
    import com.demo.confin.result.MapResultHandler;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.ibatis.executor.Executor;
    import org.apache.ibatis.mapping.MappedStatement;
    import org.apache.ibatis.plugin.Interceptor;
    import org.apache.ibatis.plugin.Intercepts;
    import org.apache.ibatis.plugin.Invocation;
    import org.apache.ibatis.plugin.Signature;
    import org.apache.ibatis.session.ResultHandler;
    import org.apache.ibatis.session.RowBounds;
    
    import java.util.Properties;
    
    /**
     * @author
     * @date 2022-10-21 23:35
     * @since 1.8
     */
    @Slf4j
    @Intercepts({
            @Signature(method = "query",type = Executor.class,args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
    })
    public class OneInterceptor implements Interceptor {
    
        /**
         * 可以设为有参构造,并在舒适化时传入一些参数;
         * 如果用依赖诸如的方式则要注意注入顺序
         */
        public OneInterceptor(){
    
        }
    
        /**
         *
         * 取出 MappedStatement 并根据 ID 过滤我们要处理的查询方法(全部处理则无需过滤)
         * mapper.xml 不可重载,ID 即包名.类名.方法名,MappedStatement 即 XML 配置文件内某个 SQL 标签的封装类
         *
         * @param invocation
         * @return
         * @throws Throwable
         */
        @Override
        public Object intercept(Invocation invocation) throws Throwable {
    
            log.info("Proxy {} Target {}",this.getClass().getName(),invocation.getTarget().getClass());
    
            //我们签名的方法第一个参数为MappedStatement
            MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
            String id = mappedStatement.getId();
            //比如只处理按名字查询
            String queryByName = "com.demo.mapper.AnimalMapper.queryByName";
            if (queryByName.equals(id)){
                //修改参数
                invocation.getArgs()[1] = "兔";
                //添加自定义处理器
                MapResultHandler mapResultHandler = new MapResultHandler();
                invocation.getArgs()[3] = mapResultHandler;
                //调用
                invocation.proceed();
                //返回
                return mapResultHandler.getResult();
            }
            return invocation.proceed();
        }
    
        @Override
        public void setProperties(Properties properties) {
            Interceptor.super.setProperties(properties);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73

    启动服务,并调用接口(顺便测试参数修改):127.0.0.1:8093/animal/queryByName/测试

    在这里插入图片描述

    4.为不同SQL动态设置超时

    package com.demo.confin;
    
    import com.alibaba.druid.pool.DruidPooledPreparedStatement;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.ibatis.executor.statement.RoutingStatementHandler;
    import org.apache.ibatis.executor.statement.StatementHandler;
    import org.apache.ibatis.mapping.BoundSql;
    import org.apache.ibatis.plugin.Interceptor;
    import org.apache.ibatis.plugin.Intercepts;
    import org.apache.ibatis.plugin.Invocation;
    import org.apache.ibatis.plugin.Signature;
    import org.apache.ibatis.session.ResultHandler;
    
    import java.sql.Statement;
    import java.util.Map;
    import java.util.Properties;
    
    /**
     * @author 
     * @date 2022-10-21 23:49
     * @since 1.8
     */
    @Slf4j
    @Intercepts({
            @Signature(method = "query", type = StatementHandler.class, args = {Statement.class, ResultHandler.class})
    })
    public class DynamicSetSqlTimeOutInterceptor implements Interceptor {
    
    
        /**
         * TODO
         * 1.定义 Filed,如超时值
         * 2.通过依赖注入、有参构造初始化 Filed
         */
    
        /**
         * 附加超时
         */
        private int addedTime;
    
        /**
         * 构造初始化
         * @param addedTime
         */
        public DynamicSetSqlTimeOutInterceptor(int addedTime){
            this.addedTime = addedTime;
        }
    
        /**
         * 调用存储过程时获取参数的超时配置并重置到SQL配置里
         * @param invocation
         * @return
         * @throws Throwable
         */
        @Override
        public Object intercept(Invocation invocation) throws Throwable {
    
            //获取连接信息
            DruidPooledPreparedStatement statement = (DruidPooledPreparedStatement) invocation.getArgs()[0];
    
            //TODO 通过SQL内是否包含某些特殊字符判断是否需要动态设置超时
            if (statement.getSql().startsWith("")){
    
                //取参数
                RoutingStatementHandler target = (RoutingStatementHandler)invocation.getTarget();
                BoundSql boundSql = target.getBoundSql();
                Map param = (Map) boundSql.getParameterObject();
                //TODO 获取超时参数(通过SQL参数传递)
                try {
                    int timeOut = (int) param.get("sqlTimeOut");
                    //设置超时
                    statement.setQueryTimeout(timeOut + addedTime);
                    //TODO 缓存超时信息并通过 close 或 cancel 方法主动取消查询 statement statement.close(); statement.cancel();
                    Object proceed = invocation.proceed();
                    return proceed;
                } finally {
                    //TODO 收尾处理
                    log.info("处理完成...");
                }
            }
    
            //非存储过程调用直接返回
            return invocation.proceed();
    
        }
    
        @Override
        public void setProperties(Properties properties) {
            Interceptor.super.setProperties(properties);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92

    此处不在演示效果,可通过慢查询或调用存储过程设置超时,测试效果

  • 相关阅读:
    mindspore课程打卡第二天
    数据类型概括
    论文阅读_大模型_ToolLLM
    浅谈智能化能源管理系统平台在企业中的应用
    MySQL与Oracle数据库通过系统命令导出导入
    【JavaEE进阶】——SpringBoot 统⼀功能处理
    net/http完全超时手册
    java线上投保的设计计算机毕业设计源码
    近年来国内室内定位领域硕士论文选题的现状与趋势
    【C++】VS建立完整的项目结构
  • 原文地址:https://blog.csdn.net/weixin_42176639/article/details/127354804