在本地开发调试过程中,有时候需要输出PreparedStatement 的调用的SQL,也就是将每一个SQL调用语句都输出,按照文档的介绍,可以在配置中添加如下日志输出的配置。
# 配置slq打印日志
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
然而,可以观察日志,知道日志是分别输出若干部分带有"?"参数的SQL语句,也就是需要我们人手去替换"?",这样才可以得到一个完整可以执行的SQL,这个并不是我们要的,我们需要的是一个可以直接输出完整SQL的方法。
Mybatis-plus 提供了类似于mybatis原生的拦截器一样的拦截器实现,我们只需要模仿官方提供的分页插件,在其之后,添加一个替换完"?"输出SQL的拦截器既可。
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MybatisPlusConfig {
/**
* 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
interceptor.addInnerInterceptor(new MyBatisPlusSqlLogInterceptor());
return interceptor;
}
@Bean
public ConfigurationCustomizer configurationCustomizer() {
return configuration -> configuration.setUseDeprecatedExecutor(false);
}
}
//MyBatisPlusSqlLogInterceptor
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.SQLException;
import java.text.DateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.regex.Matcher;
/*
*创建者: LSZ
*创建时间: 2021-01-19 21:58:10
*描述: 用于输出格式化好的SQL
*/
public class MyBatisPlusSqlLogInterceptor implements InnerInterceptor {
private static Logger logger = LoggerFactory.getTraceLogger(MyBatisPlusSqlLogInterceptor.class);
private static boolean printSQL = false;
public static void startPrintSQL(){
printSQL = Boolean.TRUE;
}
@Override
public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
if(printSQL) {
MappedStatement mappedStatement = ms;
String sqlId = mappedStatement.getId();
Configuration configuration = mappedStatement.getConfiguration();
String sql = getSql(configuration, boundSql, sqlId);
logger.info(sql);
}
}
@Override
public void beforeUpdate(Executor executor, MappedStatement ms, Object parameter) throws SQLException {
if(printSQL) {
MappedStatement mappedStatement = ms;
String sqlId = mappedStatement.getId();
Configuration configuration = mappedStatement.getConfiguration();
BoundSql boundSql = mappedStatement.getBoundSql(parameter);
String sql = getSql(configuration, boundSql, sqlId);
logger.info(sql);
}
}
public static String getSql(Configuration configuration, BoundSql boundSql, String sqlId ) {
try {
String sql = showSql(configuration, boundSql);
StringBuilder str = new StringBuilder(100);
str.append(sqlId);
str.append(" ==> ");
str.append(sql);
str.append(";");
return str.toString();
} catch(Error e) {
logger.error("解析 sql 异常", e);
}
return "";
}
public static String showSql(Configuration configuration, BoundSql boundSql) {
Object parameterObject = boundSql.getParameterObject();
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
String sql = boundSql.getSql().replaceAll("[\\s]+", " ");
if (parameterMappings != null && parameterMappings.size() > 0 && parameterObject != null) {
TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(parameterObject)));
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
for (ParameterMapping parameterMapping : parameterMappings) {
String propertyName = parameterMapping.getProperty();
if (metaObject.hasGetter(propertyName)) {
Object obj = metaObject.getValue(propertyName);
sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(obj)));
} else if (boundSql.hasAdditionalParameter(propertyName)) {
Object obj = boundSql.getAdditionalParameter(propertyName);
sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(obj)));
}
}
}
}
return sql;
}
private static String getParameterValue(Object obj) {
String value = null;
if (obj instanceof String) {
value = "'" + obj.toString() + "'";
} else if (obj instanceof Date) {
DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.CHINA);
value = "'" + formatter.format(obj) + "'";
}else if(obj instanceof LocalDate){
value = "'" + ((LocalDate) obj).format( DateTimeFormatter.ofPattern("yyyy-MM-dd")) + "'";
}else if(obj instanceof LocalDateTime){
value = "'" + ((LocalDateTime) obj).format( DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) + "'";
} else {
if (obj != null) {
value = obj.toString();
} else {
value = "";
}
}
return value;
}
}
该插件,可以加在测试用例环节或者开发环境中将其打开,以便输出SQL。原理其实就是简单的替换参数并组装成SQL输出而已。
版权声明:本文为linsongze2011原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。