mybatis自定义日志实现

本文介绍了如何在MyBatis中通过自定义logImpl属性,避免拦截器对性能的影响,同时保持SQL日志记录。作者演示了如何配置SLF4J和自定义日志类,以及mybatis-plus框架下的配置方法。
摘要由CSDN通过智能技术生成

网上挺多文章是通过mybatis的拦截器来实现的,但是测试发现,sql条数如果过多的话,相比于mybatis原生的输出方式,通过拦截器方式输出日志会对性能有影响。当然,因为mybatis原生的输出级别是debug,在有些情况下可能不适用(比如有的系统只保存INFO、ERROR级别的日志,但又想要保留SQL日志方便排查问题),所以这里通过自定义日志来达到不通过拦截器且能输出日志的目的。

mybatis的xml方式配置(spring中properties等其他方式也是类似的,都是logImpl属性,源码也差不多)

建了一张hobby表来做测试,大家可以用自己的表
在这里插入图片描述
Hobby实体类、mapper和xml,都是很常用的,方便起见都放在一个文件夹下面,大家可以使用自己项目中的
在这里插入图片描述
在这里插入图片描述

mybatis-config配置内容

<?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>
    <settings>
        <!-- 指定使用SLF4J框架-->
        <setting name="logImpl" value="SLF4J"/>
    </settings>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <!-- 这些都要改成自己的-->
                <property name="url" value="jdbc:mysql://xxx.xxx.xxx.xxx:3306/test1?serverTimezone=Asia/Shanghai"/>
                <property name="username" value="root"/>
                <property name="password" value="root/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper resource="HobbyMapper.xml"/>
    </mappers>
</configuration>

首先我们看一下原生的mybatis输出(配置使用了SLF4J日志框架),这边我已经自己建了一个测试项目,大家可以用自己的项目,表也可以随便用。

public class MybatisTest {
    @SneakyThrows
    public static void main(String[] args) {
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        HobbyMapper mapper = sqlSession.getMapper(HobbyMapper.class);
        System.out.println(mapper.queryAll());
        System.out.println(mapper.getById(1));
        sqlSession.close();
    }
}

可以看到,未自定义日志输出之前,都是DEBUG级别的日志,如果把mybatis-config.xml中的<setting name="logImpl" value="SLF4J"/>去掉,是没有这些日志输出的,所以,可以发现,通过mybatis的logImpl属性,我们是可以来指定日志对象的。

mybatis中文网原话:可选的值有:SLF4J、LOG4J、LOG4J2、JDK_LOGGING、COMMONS_LOGGING、STDOUT_LOGGING、NO_LOGGING,或者是实现了 org.apache.ibatis.logging.Log 接口,且构造方法以字符串为参数的类完全限定名

在这里插入图片描述
所以我们只需要自定义一个类,实现org.apache.ibatis.logging.Log 接口,重写其中的方法就行,可以看到,里面对应了4种级别的日志输出方法
在这里插入图片描述
这里我们自定义一个CsLog类

import org.apache.ibatis.logging.Log;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class CsLog implements Log {
	/**
	 * 定义一个log属性,在构造器中初始化,方便后续的使用
	 */
    private final Logger log;

    /**
     * 定义一个带参构造函数,拿到全类名之后就可以通过日志工厂拿到log对象了,我这里使用的slf4j,其他的日志框架也差不多用法
     *
     * @param className 类名称
     * @see org.apache.ibatis.logging.LogFactory#setImplementation(Class)
     */
    public CsLog(String className) {
        log = LoggerFactory.getLogger(className);
    }

    @Override
    public boolean isDebugEnabled() {
        return log.isDebugEnabled();
    }

    @Override
    public boolean isTraceEnabled() {
        return log.isTraceEnabled();
    }

    @Override
    public void error(String s, Throwable e) {
        log.error(s, e);
    }

    @Override
    public void error(String s) {
        log.error(s);
    }

    @Override
    public void debug(String s) {
    	// 为了区别之前的,这里加个前缀,并且改成info级别,大家可根据需要修改
        log.info("自定义的logImpl" + s);
    }

    @Override
    public void trace(String s) {
        log.trace("自定义的logImpl" + s);
    }

    @Override
    public void warn(String s) {
        log.warn("自定义的logImpl:" + s);
    }
}

然后在mybatis-config.xml中配置,之前我配置的是SLF4J,把SLF4J改成自定义日志类的全类名就行

<setting name="logImpl" value="mybatis.test.CsLog"/>

改完之后运行截图,可以发现输出的日志已经是自定义的了
在这里插入图片描述

mybatis-plus方式配置

如果使用的是mybatis-plus框架,也可以指定这个属性

mybatis-plus:
  configuration:
    log-impl: mybatis.test.CsLog

原理

org.apache.ibatis.builder.xml.XMLConfigBuilder类中parseConfiguration方法中会调用loadCustomLogImpl方法
在这里插入图片描述
获取logImpl属性值,然后拿到日志实现类的Class对象,拿到之后再通过configuration对象的方法去设置
在这里插入图片描述
resolveClass方法里面会根据这个属性值去已经注册的别名里面查询是否存在别名,存在就返回别名map集合中的value(所以为什么前面写一个SLF4J就可以使用slf4j输出日志了,就是因为mybatis内部已经定义好了别名),不存在就通过Class.forName返回一个Class对象
在这里插入图片描述
在这里插入图片描述
前面一定会拿到Class对象,如果拿不到会报错,拿到之后,就会调用org.apache.ibatis.session.Configuration对象中的setLogImpl方法,方法里面主要是调用日志工厂的useCustomLogging方法,顾名思义就是使用自定义的日志实现
在这里插入图片描述
最终会调用到LogFactory中的setImplementation方法,其他几个UseSlf4JLogging()类似的都是调用会setImplementation方法。这个方法里面会拿到自定义日志的构造器,这就是为什么前面要自定义一个有参构造器的原因了。拿到构造器之后会赋值给logConstructor,这是因为后续其他mapper的log也要初始化(通过此类的getLog方法),所以为什么我们输出sql的时候,显示的都是sql所在的全类名,而不是固定的类名。
在这里插入图片描述在这里插入图片描述

题外话:LogFactory里面static代码块会去找存不存在日志框架,里面会先判断logConstructor属性是否为空,这就是为什么不配置logImpl属性的时候,也会输出日志了原因了
在这里插入图片描述

MyBatis提供了自定义拦截器的功能,可以在执行SQL语句前后进行一些自定义的操作,比如日志记录、权限校验等。要实现自定义拦截器,你需要按照以下步骤进行操作: 1. 创建一个Java类,实现`Interceptor`接口。这个接口定义了MyBatis拦截器的方法。 ```java public class MyInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { // 在执行SQL语句前后进行一些自定义操作 // 这里可以写你的逻辑代码 return invocation.proceed(); // 继续执行下一个拦截器或目标对象的方法 } @Override public Object plugin(Object target) { return Plugin.wrap(target, this); // 使用当前拦截器包装目标对象 } @Override public void setProperties(Properties properties) { // 设置一些属性值 } } ``` 2. 在MyBatis配置文件(比如`mybatis-config.xml`)中配置自定义拦截器。 ```xml <configuration> <!-- 其他配置 --> <plugins> <plugin interceptor="com.example.MyInterceptor"> <!-- 可以设置一些属性值 --> </plugin> </plugins> </configuration> ``` 注意,`com.example.MyInterceptor`是你自己实现的拦截器类的全限定名。 通过以上步骤,你就可以实现自定义拦截器了。当MyBatis执行SQL语句时,会先调用你的拦截器的`intercept`方法,在该方法内部你可以编写你想要的逻辑。还可以通过`plugin`方法对目标对象进行包装,以实现多个拦截器的链式调用。 希望能帮到你!如有更多问题,请继续提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值