SpringBoot Mybatis-Plus 日志带参数

发布于:2024-07-05 ⋅ 阅读:(158) ⋅ 点赞:(0)

SpringBoot Mybatis-Plus 日志带参数

MyBatis-Plus 是 MyBatis 的增强工具,它在 MyBatis 的基础上提供了许多便捷功能,其中之一就是拦截器(Interceptor)。拦截器在 MyBatis-Plus 中用于对 SQL 操作进行拦截和修改,例如实现分页、数据权限控制、SQL 日志记录等功能。下面是 MyBatis-Plus 拦截器的基本原理和工作机制:

MyBatis 拦截器的基本原理
MyBatis 拦截器是通过 Java 动态代理机制实现的。拦截器可以拦截 MyBatis 的四种核心对象的方法执行,并在方法执行前后进行处理:

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

MyBatis-Plus 拦截器的实现
MyBatis-Plus 基于 MyBatis 的拦截器机制,提供了一些默认实现,如分页拦截器、性能分析拦截器等。以下是 MyBatis-Plus 拦截器的实现原理:

方法 实现原理
注册拦截器 拦截器需要在 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 {
	...
}

在 MyBatis 中,Executor 接口定义了多个方法,这些方法用于执行各种类型的 SQL 操作。以下是 Executor 接口中常见的方法:

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

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) {
    }

    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

网站公告

今日签到

点亮在社区的每一天
去签到