Java mybatis-plus的sql语句打印问题小结

1. 问题背景

在使用Java的mybatis-plus框架进行开发时,有时候会遇到需要打印SQL语句的情况,以便于调试和排查问题。mybatis-plus提供了默认的打印SQL语句的方式,但是有时候会发现打印出来的SQL语句并不符合预期或者不够详细,因此我们需要对打印方式进行定制和调整,以满足实际的开发需求。

2. 默认打印方式

在mybatis-plus中,可以通过设置log4j的日志级别来控制是否打印SQL语句,打印SQL语句的日志级别为DEBUG。mybatis-plus默认的打印方式可以通过设置mybatis-plus的全局配置对象进行配置,具体代码如下:

GlobalConfig globalConfig = new GlobalConfig();

globalConfig.setSqlInjector(new LogicSqlInjector());

globalConfig.setDbConfig(

new GlobalConfig.DbConfig()

.setDbType(DbType.MYSQL)

.setLogicDeleteValue("0")

.setLogicNotDeleteValue("1")

);

globalConfig.setSqlParserCache(true);

globalConfig.setBanner(false);

globalConfig.setMetaObjectHandler(new MyMetaObjectHandler());

globalConfig.setRefresh(true);

globalConfig.setSqlRunner(new MySqlRunner());

globalConfig.setSqlRunner(new SqlServerRunner());

globalConfig.setSqlRunner(new OracleRunner());

globalConfig.setSqlRunner(new PostgreSqlRunner());

globalConfig.setSqlRunner(new H2Runner());

globalConfig.setSqlRunner(new SqliteRunner());

globalConfig.setSqlRunner(new DerbyRunner());

globalConfig.setSqlRunner(new HSqlRunner());

globalConfig.setSqlRunner(new MariaDBRunner());

globalConfig.setSqlRunner(new SophiaRunner());

globalConfig.setPerformanceInterceptor(new PerformanceInterceptor());

globalConfig.setSqlInLog(true);

globalConfig.setIdentifierGenerator(new CustomIdGenerator());

globalConfig.setDbType(DbType.MYSQL);

globalConfig.setDbConfig(

new GlobalConfig.DbConfig()

.setAutoCommit(false)

);

globalConfig.setSqlSessionFactoryBuilder(new SqlSessionFactoryBuilder());

globalConfig.setSqlSessionFactory(new SqlSessionFactoryBuilder().build(inputStream));

在上述配置代码中,我们可以看到有一个属性叫作"sqlInLog",它的作用就是控制是否打印SQL语句。如果将这个属性设置为true,则mybatis-plus会在DEBUG级别下打印SQL语句到日志中。

2.1 示例

例如,我们有一个简单的查询方法:

@Override

public List list() {

QueryWrapper<User> wrapper = new QueryWrapper<>();

wrapper.lambda()

.eq(User::getEnabled, true)

.orderByDesc(User::getCreatedTime);

return userDao.selectList(wrapper);

}

默认情况下mybatis-plus打印出来的SQL语句如下所示:

SELECT id,name,age,enabled,created_time FROM user WHERE (enabled = ?) order by created_time desc

可以看到,打印出来的SQL语句并不包括具体的参数值,这在调试时可能会比较困难。

3. 定制SQL打印方式

由于mybatis-plus的默认打印方式无法满足实际的开发需求,我们需要对其进行定制和调整。下面介绍两种常用的定制方式。

3.1 方式一:使用日志框架打印参数

通过查看mybatis-plus的源码,我们可以发现mybatis-plus对于打印SQL参数的处理是通过log4j的消息格式化机制来实现的。因此,我们可以通过调整log4j的消息格式化模板,来实现定制化的SQL打印方式。

在log4j的配置文件中,添加如下配置:

<appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">

<layout class="org.apache.log4j.PatternLayout">

<param name="ConversionPattern" value="%d %-5p %c{1}:%L - %m%n %X{params}\n"/>

</layout>

</appender>

在这个配置文件中,我们将log4j的消息格式化模板中的%X{params}表示可以打印出SQL参数信息。具体到mybatis-plus中,我们需要在代码中将参数信息保存到MDC(Mapped Diagnostic Context)中,代码如下所示:

@Override

public List list() {

QueryWrapper<User> wrapper = new QueryWrapper<>();

wrapper.lambda()

.eq(User::getEnabled, true)

.orderByDesc(User::getCreatedTime);

MDC.put("params", wrapper.getParamNameValuePairs().toString());

try {

return userDao.selectList(wrapper);

}finally {

MDC.remove("params");

}

}

这里我们使用MDC的方式来保存SQL参数信息,当查询结束后再将其移除,以免对其他查询产生干扰。这样配置后,在调用上述的list方法时,日志中将会打印出SQL参数信息,例如:

SELECT id,name,age,enabled,created_time FROM user WHERE (enabled = 1) order by created_time desc

可见,打印出来的SQL语句包含了具体的参数值信息,方便我们进行调试。

3.2 方式二:使用Interceptor拦截器打印参数

除了通过日志框架调整输出格式以外,我们还可以通过使用mybatis-plus提供的Interceptor拦截器来实现SQL打印方式的定制化。Interceptor拦截器是mybatis-plus提供的一个扩展机制,可以对SQL执行过程中的各个环节进行拦截和处理。下面是在mybatis-plus中添加Interceptor的方式:

@Bean

public SqlSessionFactory sqlSessionFactory() throws Exception {

MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();

sqlSessionFactory.setDataSource(dataSource);

Interceptor[] interceptors = new Interceptor[]{

new SqlPrintInterceptor(), //添加一个SQL打印拦截器

};

sqlSessionFactory.setPlugins(interceptors);

return sqlSessionFactory.getObject();

}

使用Interceptor打印SQL语句的关键在于实现SqlPrintInterceptor拦截器。下面是SqlPrintInterceptor拦截器的实现代码:

import org.apache.ibatis.executor.statement.StatementHandler;

import org.apache.ibatis.plugin.*;

import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;

import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;

import java.sql.Statement;

import java.util.Properties;

/**

* SQL打印拦截器

*/

@Intercepts({

@Signature(type = StatementHandler.class, method = "prepare", args = {Statement.class, Integer.class})

})

public class SqlPrintInterceptor implements Interceptor {

@Override

public Object intercept(Invocation invocation) throws Throwable {

StatementHandler statementHandler = (StatementHandler) invocation.getTarget();

Object parameterObject = statementHandler.getParameterHandler().getParameterObject();

String sql = statementHandler.getBoundSql().getSql();

System.out.println("SQL: " + sql);

return invocation.proceed();

}

@Override

public Object plugin(Object target) {

return Plugin.wrap(target, this);

}

@Override

public void setProperties(Properties properties) {

// do nothing

}

}

这个拦截器的实现比较简单,只需要在prepare方法中获取SQL语句并打印出来即可。

3.3 示例

通过使用方式二中的拦截器,在调用上述的list方法时,日志中将会打印出SQL语句,例如:

SELECT id,name,age,enabled,created_time FROM user WHERE (enabled = ?) order by created_time desc; - parameter: true

这里我们可以看到,打印出来的SQL语句还包含了参数信息,这对于调试和排查问题非常有帮助。

4. 结论

通过本文的介绍,读者可以了解到mybatis-plus的默认SQL打印方式以及两种定制化的SQL打印方式。在实际的开发中,我们可以根据不同的需求选择不同的定制方式,以满足自身的需求。同时,本文的介绍也让我们了解到mybatis-plus的一些内部实现机制,这些知识对于我们深入理解mybatis-plus的使用和内部实现都是非常有帮助的。

数据库标签