MyBatis拦截器执行顺序

2021-02-08 19:24:55 964

最近项目用上了mybatis, 但是想像hibernate那样能打印sql, 于是写了个基于mybatis拦截器的sql打印, 参考这个 https://blog.22xcode.com/article/537103218711199745

然后, 碰到了问题, 拦截器会重复输出一句sql

mybatis sql: SELECT id, name FROM user

mybatis sql: SELECT id, name FROM user

排查下, 项目里跟mybatis有关的就只有pagehelper了, 猜测可能是pagehelper 为了分页再发了一条算总数count的sql, 而打印sql的拦截器没有获取到count查询的完整sql, 所以看上去发了两遍一样的sql

网上找了下拦截器的资料, 发现基本都是这个样子

1.png

大概意思就是后定义/加载的拦截器会先执行.

于是更改配置, 将sql打印拦截器在pagehelper的拦截器之后执行

	@Autowired
    private List<SqlSessionFactory> sqlSessionFactoryList;
    
 	/**
     * 在分页拦截器后加载
     */
    @Autowired
    PageHelperAutoConfiguration pageHelperAutoConfiguration;

    @PostConstruct
    public void registerInterceptor() {
        if (enableSqlLog) {
            MybatisSqlLogInterceptor interceptor = new MybatisSqlLogInterceptor();
            for (SqlSessionFactory sqlSessionFactory : sqlSessionFactoryList) {
                Configuration configuration = sqlSessionFactory.getConfiguration();
                if (!containsInterceptor(configuration, interceptor)) {
                    configuration.addInterceptor(interceptor);
                }
            }
            log.warn("[mybatis sql log]已启用, 请检查当前是否为开发环境");
        } else {
            log.info("[mybatis sql log]已禁用");
        }
    }

其实也试了下@AutoConfigureAfter, 但是发现不起作用, 后续再琢磨下吧.

配置完成, 再次执行查询, sql只打印一次, 说明sql打印拦截器在分页拦截器之前被调用, 完成

但是为什么先加载的拦截器反而后执行, 网上好像没啥资料, 那就只能自己翻翻源码了.

debug看了下, 找到了这个类

package org.apache.ibatis.plugin;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class InterceptorChain {

  private final List<Interceptor> interceptors = new ArrayList<>();

  public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors) {
      target = interceptor.plugin(target);
    }
    return target;
  }

  public void addInterceptor(Interceptor interceptor) {
    interceptors.add(interceptor);
  }

  public List<Interceptor> getInterceptors() {
    return Collections.unmodifiableList(interceptors);
  }

}

InterceptorChain顾名思义, 拦截链, 可以看到项目定义的拦截器都在InterceptorChain内用ArrayList存储起来

核心是这个InterceptorChain#pluginAll这个方法

解析下

pluginAll方法中, 遍历interceptors, 为目标对象创建代理.

即, 在这一步, 先加载的拦截器会优先被遍历, 会优先对目标对象进行代理,

后加载的拦截器, 在原有的代理之上再进行代理, 一层包一层, 类似洋葱.

当方法被调用时, 会先执行最外层的代理方法.

所以, 先加载到的拦截器, 反而是最后执行.


MyBatis处理一对多关系时的性能考虑

Mybatis框架对于处理一对多的情况有两种方法查询的时候JOIN子表, 然后交给MyBatis拼装数据查询的时候不JOIN子表, 主表查询完成后发起select再查询关联表数据, 还可以配置fetchType=lazy进行懒加载这两种方法各有问题:第一种方案有两个缺陷: 1) 做分页查询的时候不准
2022-02-11

模拟Mybatis"无实现类"调用接口

Mybatis让我们通过接口(interface)就能调用到对应sql, 看起来好像没有实现类, 貌似不符合"常识"通过这个简易demo, 带你大致了解mybatis背后所做的事先建立maven工程 <parent> <groupId>org.springframework.b
2021-02-21

MyBatis是如何让我们通过接口就能调用到SQL的

大致可分为如下几个步骤1. 动态注册bean1.1 根据配置mapperScan, 扫描对应的包, 将对应的类解析成BeanDefinition1.2 通过替换BeanDefinition中的BeanClass为MapperFactoryBean, (原来的BeanClass是Mapper接口) 实
2021-02-13

MyBatis拦截器实现SQL打印

mybatis有自带的sql打印, 但只会出现在抛异常的时候, 或者配置日志输出, 但是输出的日志较为冗长像这样### Error querying database. Cause: java.lang.ArithmeticException: / by zero ### The error ma
2021-02-08
MyBatis拦截器执行顺序

MyBatis拦截器执行顺序

最近项目用上了mybatis, 但是想像hibernate那样能打印sql, 于是写了个基于mybatis拦截器的sql打印, 参考这个https://blog.22xcode.com/post/78然后, 碰到了问题, 拦截器会重复输出一句sqlmybatis sql: SELECT id, na
2021-02-08

freemarker 时间显示不正常 设置时区

项目在本地开发的时候显示正常,部署上服务器就一直差8个小时,最后发现freemarker官方文档有这样的说明time_zone:时区的名称来显示并格式化时间。 默认情况下,使用JVM的时区。 也可以是 Java 时区 API 接受的值,或者 "JVM default" (从 FreeMarker 2
2020-03-28
IDEA 2019.1 xml 不高亮

IDEA 2019.1 xml 不高亮

前几天更新了idea后,发现xml里的代码都没有了高亮,变得跟记事本一个德性了打开setting ,搜索 File Types,找到xml项, 查看下方的匹配格式,果然没有xml,(idea真是厉害)点击右方的+,输入*.xml,点击ok,解决问题
2020-03-28

npm install 淘宝镜像

npm install --registry=https://registry.npm.taobao.org
2020-03-28
Java中方法的参数传递机制

Java中方法的参数传递机制

来看一段代码 public class Man { private String name; private Integer age; public String getName() { return name; } publi
2020-03-28
基于自定义注解手写权限控制

基于自定义注解手写权限控制

方法一: AOP 方法二: 拦截器项目结构项目依赖<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-w
2020-03-28

Docker 部署 详细全过程 附代码

Docker 部署本站 全过程环境:CentOS7.61. 安装Docker其他版本CentOS可以参考这个https://help.aliyun.com/document_detail/187598.html查看本机内核版本,内核版本需高于 3.10uname -r 确保 yum 包最新yum u
2020-03-28

SpringBoot 启动普通java工程

引入依赖<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>2.0.9</version> </dependency>
2020-03-28

Vue.js DOM操作

<template> <input type="button" @click="reply($event)" value="回复"> </template> export default { methods: { replyFun(e) {
2020-03-29
CentOS7编译调试OpenJDK12

CentOS7编译调试OpenJDK12

1. 下载源码https://hg.openjdk.java.net/jdk/jdk12点击左侧的browse,再点击zip,就可以下载zip格式的源码压缩包。unzip xxx.zip 解压文件2. 安装jdkyum install java-11-openjdk-devel -y3. 运行con
2020-04-23
编写自己的Spring Boot Starter

编写自己的Spring Boot Starter

1.新建一个maven项目命名规则统一是xxx-spring-boot-starter完整pom.xml<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0"
2020-06-29