SpringBoot Mybatis-Plus 日志带参数

SpringBoot Mybatis-Plus 日志带参数

在Spring Boot中,MyBatis插件机制通过拦截器(Interceptor)来实现。拦截器允许开发人员在执行SQL语句的各个阶段(如SQL语句创建、参数处理、结果映射等)插入自定义逻辑。MyBatis的拦截器主要用于增强MyBatis的功能,如日志记录、性能监控、实现分页、数据权限控制、SQL日志、动态SQL生成等,在SQL执行之前后,提供了四个拦截点,在方法执行前后进行处理,支持不同场景的功能扩展。

作用
Executor 用于执行增/删/改/查等数据库操作。
ParameterHandler 用于处理 SQL 参数。
ResultSetHandler 用于处理结果集。
StatementHandler 用于处理 SQL 语句的创建和参数化。

@Signature参数说明

参数 参数说明
type 就是指定拦截器类型(Executor, ParameterHandler, StatementHandler, ResultSetHandler)。
method 是拦截器类型中的方法,不是自己写的方法(update, query, prepare …)。
args 是method中方法的入参。

Executor接口中常见的方法

方法 实现原理
update 执行插入、更新和删除操作。
query 执行查询操作,返回结果集。
flushStatements 刷新批量执行的 SQL 语句。
commit 提交事务。
rollback 回滚事务。
getTransaction 获取当前事务对象。
close 关闭执行器。
isClosed 检查执行器是否已关闭。
clearLocalCache 清空本地缓存。

Mybatis拦截器的实现原理

方法 实现原理
注册拦截器 拦截器需要在 MyBatis 配置文件中注册,通常是在 mybatis-config.xml 中配置,也可以通过 Java 配置类进行注册。
实现 Interceptor 接口 自定义拦截器需要实现 MyBatis 的 Interceptor 接口,并重写 intercept 方法。这个方法会接收一个 Invocation 对象,表示被拦截的方法调用。
调用链 MyBatis 在执行 SQL 操作时,会按照拦截器的配置顺序,依次调用每个拦截器的 intercept 方法。拦截器可以选择在方法执行前后进行处理,或者直接修改方法参数和返回值。
@Intercepts({
    @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
    @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class MyInterceptor implements Interceptor {
	...
}

1 实现代码

import java.text.DateFormat;
import lombok.extern.slf4j.Slf4j;

import cn.hutool.core.collection.CollUtil;

import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import java.util.regex.Matcher;

import java.sql.Connection;

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;

import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.type.TypeHandlerRegistry;

@Slf4j
@Component
@ConditionalOnProperty(prefix = "mybatis-plus", name = "customer-log", havingValue = "true")
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class MybatisLogger implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler handler = (StatementHandler) invocation.getTarget();
        MetaObject object = MetaObject.forObject(handler,
                SystemMetaObject.DEFAULT_OBJECT_FACTORY,
                SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY,
                new DefaultReflectorFactory());
        MappedStatement statement = (MappedStatement) object.getValue("delegate.mappedStatement");
        log.info("SQL执行类: {}", statement.getId());
        log.info("SQL执行类型: {}", statement.getSqlCommandType().toString());

        BoundSql bound = handler.getBoundSql();
        Configuration configuration = statement.getConfiguration();
        String sql = getFullSql(configuration, bound);
        log.info("SQL语句: {}", sql);

        long start = System.currentTimeMillis();
        Object value = invocation.proceed();
        long end = System.currentTimeMillis();
        log.info("SQL耗时: {}", (end - start));
        return value;
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
    	// 可以通过MyBatis配置文件或注解传递属性
    }

    public String getFullSql(Configuration conf, BoundSql bound) {
        Object object = bound.getParameterObject();
        List<ParameterMapping> list = bound.getParameterMappings();
        String sql = bound.getSql().replaceAll("[\\s]+", " ").toLowerCase(Locale.ROOT);
        if (CollUtil.isNotEmpty(list) && object != null) {
            TypeHandlerRegistry type = conf.getTypeHandlerRegistry();
            if (type.hasTypeHandler(object.getClass())) {
                sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParaValue(object)));
            } else {
                MetaObject meta = conf.newMetaObject(object);
                for (ParameterMapping parameterMapping : list) {
                    String name = parameterMapping.getProperty();
                    if (meta.hasGetter(name)) {
                        Object obj = meta.getValue(name);
                        sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParaValue(obj)));
                    } else if (bound.hasAdditionalParameter(name)) {
                        Object obj = bound.getAdditionalParameter(name);
                        sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParaValue(obj)));
                    } else {
                        sql = sql.replaceFirst("\\?", "缺失");
                    }
                }
            }
        }
        return sql;
    }

    private String getParaValue(Object obj) {
        if (obj instanceof String) {
            return "'" + obj + "'";
        } else if (obj instanceof Date) {
            DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.CHINA);
            return "'" + formatter.format(new Date()) + "'";
        } else {
            if (obj != null) {
                return obj.toString();
            } else {
                return "";
            }
        }
    }

}

2 测试结果

Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@264df4aa]
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@d91483c] was not registered for synchronization because synchronization is not active
JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@44d84b5a] will not be managed by Spring
2024-07-05 16:32:27.660  INFO 3264 --- [nio-8221-exec-1] com.xu.hander.MybatisLogger   : SQL执行类: com.xu.view.mapper.StudentMapper.selectOne
2024-07-05 16:32:27.660  INFO 3264 --- [nio-8221-exec-1] com.xu.hander.MybatisLogger   : SQL执行类型: SELECT
2024-07-05 16:32:27.660  INFO 3264 --- [nio-8221-exec-1] com.xu.hander.MybatisLogger   : SQL语句: select id,create_by,create_time,create_hour,create_date,update_by,update_time,tenant_id,user_id,cus_id,page,number,is_share,time,event from student where (cus_id = 3 and page = 3 and event = 5) order by create_time desc limit 1
2024-07-05 16:32:27.663  INFO 3264 --- [nio-8221-exec-1] com.xu.hander.MybatisLogger   : SQL耗时: 3

相关推荐

  1. SpringBoot Mybatis-Plus 日志参数

    2024-07-09 18:36:05       26 阅读
  2. Springboot自logback日志配置学习

    2024-07-09 18:36:05       64 阅读
  3. linux 13-2day 日志轮转 日志目录 轮转参数

    2024-07-09 18:36:05       54 阅读
  4. C++参数的单例模式

    2024-07-09 18:36:05       44 阅读
  5. C++:可变参数实现日志系统

    2024-07-09 18:36:05       45 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-09 18:36:05       67 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-09 18:36:05       72 阅读
  3. 在Django里面运行非项目文件

    2024-07-09 18:36:05       58 阅读
  4. Python语言-面向对象

    2024-07-09 18:36:05       69 阅读

热门阅读

  1. 测试绩效评估

    2024-07-09 18:36:05       23 阅读
  2. 【Datagear】使用参数时的If语法

    2024-07-09 18:36:05       22 阅读
  3. 实现基于Elasticsearch的搜索服务

    2024-07-09 18:36:05       27 阅读
  4. 【网络协议】ISIS

    2024-07-09 18:36:05       23 阅读
  5. 第三章 设计模式(2023版本IDEA)

    2024-07-09 18:36:05       23 阅读
  6. 命令模式在金融业务中的应用及其框架实现

    2024-07-09 18:36:05       27 阅读
  7. 【C语言】标识符大通关!

    2024-07-09 18:36:05       30 阅读
  8. Python面试题-8

    2024-07-09 18:36:05       25 阅读
  9. HPE ProLiant MicroServer Gen8加装显卡

    2024-07-09 18:36:05       23 阅读
  10. 查询进程并且杀死

    2024-07-09 18:36:05       28 阅读
  11. 预处理方法

    2024-07-09 18:36:05       27 阅读