MyBatis-Plus执行SQL分析打印

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MyBatis-Plus执行SQL分析打印相关的知识,希望对你有一定的参考价值。

参考技术A 1、本文资料来自 MyBatis-Plus官网
2、该功能依赖 p6spy 组件,完美的输出打印 SQL 及执行时长 3.1.0 以上版本
3、 示例工程

Mybatis-Plus:插件(mybatis的插件机制执行分析插件性能分析插件乐观锁插件)

本节案例承接自上节案例

1. mybatis的插件机制

MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:

  1. Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  2. ParameterHandler (getParameterObject, setParameters)
  3. ResultSetHandler (handleResultSets, handleOutputParameters)
  4. StatementHandler (prepare, parameterize, batch, update, query)

总体概括为:

  1. 拦截执行器的方法
  2. 拦截参数的处理
  3. 拦截结果集的处理
  4. 拦截Sql语法构建的处理

我们看到了可以拦截Executor接口的部分方法,比如updatequerycommitrollback等方法,还有其他接口的一些方法等。

拦截器示例:

MyInterceptor.java

package com.tian.plugins;

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;

import java.util.Properties;

@Intercepts(@Signature(	
		// 要拦截的是 Executor 执行器的 update方法
        type = Executor.class,
        method = "update",
        args = MappedStatement.class, Object.class))
public class MyInterceptor implements Interceptor 

    @Override
     //拦截方法,具体业务逻辑编写的位置 现在是只有update方法会进入该方法
    public Object intercept(Invocation invocation) throws Throwable 
        return invocation.proceed(); // invocation.proceed()是放行
    

    @Override
    // 执行sql都会进入该方法 并且会执行4次 因为有4个处理器 
    /*
    1. Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed) 拦截执行器的方法
	2. ParameterHandler (getParameterObject, setParameters) 拦截参数的处理
	3. ResultSetHandler (handleResultSets, handleOutputParameters) 拦截结果集的处理
	4. StatementHandler (prepare, parameterize, batch, update, query) 拦截Sql语法构建的处理
    */
    public Object plugin(Object target) 
        // 创建target对象的代理对象,目的是将当前拦截器加入到该对象中
        return Plugin.wrap(target, this);
    

    @Override
    public void setProperties(Properties properties) 
        //属性设置
    
 

将拦截器注入到Spring容器:


MybatisPlusConfig.java

package com.tian.springbootmybatisplus;

import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.tian.plugins.MyInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@MapperScan("com.tian.mapper") //设置mapper接口的扫描包
public class MybatisPlusConfig 
    @Bean //配置分页插件
    public PaginationInterceptor paginationInterceptor() 
        return new PaginationInterceptor();
    

    /*** 自定义拦截器 */
    @Bean
    public MyInterceptor myInterceptor() 
        return new MyInterceptor();
    

或者通过xml配置,mybatis-config.xml


2. 执行分析插件(阻断全表更新、删除的操作)

Mybatis-Plus中提供了对SQL执行的分析的插件,可用作阻断全表更新、删除的操作,注意:该插件仅适用于开发环境,不适用于生产环境。


2.1 配置

MyInterceptor.java

    @Bean //SQL分析插件
    public SqlExplainInterceptor sqlExplainInterceptor() 

        SqlExplainInterceptor sqlExplainInterceptor = new SqlExplainInterceptor();

        List<ISqlParser> list = new ArrayList<>();
        
        // 攻击 SQL 阻断解析器、加入解析链
        list.add(new BlockAttackSqlParser()); //全表更新、删除的阻断器

        sqlExplainInterceptor.setSqlParserList(list);

        return sqlExplainInterceptor;
    

BlockAttackSqlParser.java源码查看


2.2 测试

SpringbootMybatisplusApplicationTests.java

package com.tian.springbootmybatisplus;

import com.tian.mapper.UserMapper;
import com.tian.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class SpringbootMybatisplusApplicationTests 

    @Autowired
    private UserMapper userMapper;

    @Test
    public void testUpdate() 
        User user = new User();
        user.setAge(20);

        // 把所有人的age设置为20
        int result = this.userMapper.update(user, null);
        System.out.println("result = " + result);
    

运行结果:


3. 性能分析插件

性能分析拦截器,用于输出每条 SQL 语句及其执行时间,可以设置最大执行时间,超过时间会抛出异常

和执行分析插件一样,该插件只用于开发环境,不建议生产环境使用。

说明:我最开始用的Mybatis-Plus 3.3.2,发现没有PerformanceInterceptor类。于是我把Mybatis-Plus版本降到了 3.1.0 。然后就可以正常使用性能分析插件了。


3.1 Spring Boot 配置


MybatisPlusConfig.java

    @Bean
    // SQL 执行性能分析,开发环境使用,线上不推荐。
    public PerformanceInterceptor performanceInterceptor() 
        PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
        // maxTime 指的是 sql 最大执行时长, 单位为ms
        performanceInterceptor.setMaxTime(100);
        // SQL是否格式化 默认为true
        performanceInterceptor.setFormat(true);
        return performanceInterceptor;
    

编写测试用例:

SpringbootMybatisplusApplicationTests.java

package com.tian.springbootmybatisplus;

import com.tian.mapper.UserMapper;
import com.tian.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class SpringbootMybatisplusApplicationTests 

    @Autowired
    private UserMapper userMapper;

    @Test
    public void testSelectById() 
        User user = new User();
        // 查询Id为2的用户
        user.setId(2L);
        User user1 = user.selectById();
        System.out.println(user1);
    

运行结果:

异常情况补充:


3.2 配置文件方式配置

上面一直使用的代码注入Bean的方式去配置,现在我们来尝试着使用配置文件的方式去配置,这样可以练练手。

mybatis-config.xml

<?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.baomidou.mybatisplus.extension.plugins.PerformanceInterceptor">
            <!--最大的执行时间,单位为毫秒-->
            <property name="maxTime" value="100"/>
            <!--对输出的SQL做格式化,默认为false-->
            <property name="format" value="true"/>
        </plugin>
    </plugins>
</configuration>

然后在SpringBoot的全局配置文件指定Mybatis的配置文件即可:

application.properties

# 指定全局的配置文件
mybatis-plus.config-location=classpath:mybatis-config.xml

4. 乐观锁插件

当要更新一条记录的时候,希望这条记录没有被别人更新

乐观锁实现方式:

  1. 取出记录时,获取当前version
  2. 更新时,带上这个version
  3. 执行更新时, set version = newVersion where version = oldVersion
  4. 如果version不对,就更新失败

4.1 插件配置

配置文件方式:

  <!--乐观锁插件-->
        <plugin interceptor="com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor"/>

spring boot代码注入Bean的方式:


MybatisPlusConfig.java

    @Bean
    public OptimisticLockerInterceptor optimisticLockerInterceptor() 
        return new OptimisticLockerInterceptor();
    

4.2 注解实体字段

需要为实体字段添加@Version注解。

第一步,为表添加version字段,并且设置初始值为1:

Navicat执行下面的sql语句:

ALTER TABLE `tb_user` ADD COLUMN `version` int(10) NULL AFTER `email`; UPDATE `tb_user` SET `version`='1';

第二步,为User实体对象添加version字段,并且添加@Version注解:


4.3 测试

SpringbootMybatisplusApplicationTests.java

package com.tian.springbootmybatisplus;

import com.tian.mapper.UserMapper;
import com.tian.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class SpringbootMybatisplusApplicationTests 

    @Autowired
    private UserMapper userMapper;

    @Test
    public void testUpdate() 
        User user = new User();
        user.setAge(30);
        user.setId(2L);
        user.setVersion(1); // 当前的version为1

        int result = this.userMapper.updateById(user);
        System.out.println("result = " + result);
    

运行结果:

生成的SQL语句为:

  • 可以看到,更新的条件中有version条件,并且更新的version为2。
  • 如果再次执行,更新则不成功。这样就避免了多人同时更新时导致数据的不一致。

4.4 说明



以上是关于MyBatis-Plus执行SQL分析打印的主要内容,如果未能解决你的问题,请参考以下文章

springboot 打印 mybatis-plus 的sql

mybatis-plus打印完整sql语句

mybatis-plus打印完整sql语句

mybatis-plus打印完整sql语句

mybatis-plus 控制台打印sql,配置文件xml需注释掉,否则会冲突

MyBatis-Plus性能分析插件