Mybatis打印替換占位符后的完整Sql教程
利用mybtis插件打印完整的sql,將占位符?替換成實際值
import org.apache.ibatis.cache.CacheKey;
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.mapping.ParameterMode;
import org.apache.ibatis.plugin.*;
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 org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Properties;
/**
* 打印sql
*
* @date 2019/1/14 20:13
*/
@Component
@Profile({"dev", "test"})
@Intercepts({
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})}
)
public class SqlInterceptor implements Interceptor {
private static ThreadLocal<SimpleDateFormat> dateTimeFormatter = new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
}
};
@Override
public Object intercept(Invocation invocation) throws Throwable {
Object result = null;
//捕獲掉異常,不要影響業(yè)務
try {
MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
Object parameter = null;
if (invocation.getArgs().length > 1) {
parameter = invocation.getArgs()[1];
}
String sqlId = mappedStatement.getId();
BoundSql boundSql = mappedStatement.getBoundSql(parameter);
Configuration configuration = mappedStatement.getConfiguration();
long startTime = System.currentTimeMillis();
try {
result = invocation.proceed();
} finally {
long endTime = System.currentTimeMillis();
long sqlCostTime = endTime - startTime;
String sql = this.getSql(configuration, boundSql);
this.formatSqlLog(sqlId, sql, sqlCostTime, result);
}
return result;
} catch (Exception e) {
return result;
}
}
@Override
public Object plugin(Object target) {
if (target instanceof Executor) {
return Plugin.wrap(target, this);
}
return target;
}
@Override
public void setProperties(Properties properties) {
}
/**
* 獲取完整的sql語句
*
* @param configuration
* @param boundSql
* @return
*/
private String getSql(Configuration configuration, BoundSql boundSql) {
// 輸入sql字符串空判斷
String sql = boundSql.getSql();
if (StringUtil.isEmpty(sql)) {
return "";
}
return formatSql(sql, configuration, boundSql);
}
/**
* 將占位符替換成參數(shù)值
*
* @param sql
* @param configuration
* @param boundSql
* @return
*/
private String formatSql(String sql, Configuration configuration, BoundSql boundSql) {
//美化sql
sql = beautifySql(sql);
//填充占位符, 目前基本不用mybatis存儲過程調(diào)用,故此處不做考慮
Object parameterObject = boundSql.getParameterObject();
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
List<String> parameters = new ArrayList<>();
if (parameterMappings != null) {
MetaObject metaObject = parameterObject == null ? null : configuration.newMetaObject(parameterObject);
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
if (parameterMapping.getMode() != ParameterMode.OUT) {
// 參數(shù)值
Object value;
String propertyName = parameterMapping.getProperty();
// 獲取參數(shù)名稱
if (boundSql.hasAdditionalParameter(propertyName)) {
// 獲取參數(shù)值
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
// 如果是單個值則直接賦值
value = parameterObject;
} else {
value = metaObject == null ? null : metaObject.getValue(propertyName);
}
if (value instanceof Number) {
parameters.add(String.valueOf(value));
} else {
StringBuilder builder = new StringBuilder();
builder.append("'");
if (value instanceof Date) {
builder.append(dateTimeFormatter.get().format((Date) value));
} else if (value instanceof String) {
builder.append(value);
}
builder.append("'");
parameters.add(builder.toString());
}
}
}
}
for (String value : parameters) {
sql = sql.replaceFirst("\\?", value);
}
return sql;
}
/**
* 格式化sql日志
*
* @param sqlId
* @param sql
* @param costTime
* @return
*/
private void formatSqlLog(String sqlId, String sql, long costTime, Object obj) {
String sqlLog = "==> " + sql;
StringBuffer result = new StringBuffer();
if (obj instanceof List) {
List list = (List) obj;
int count = list.size();
result.append("<== Total: " + count);
} else if (obj instanceof Integer) {
result.append("<== Total: " + obj);
}
result.append(" Spend Time ==> " + costTime + " ms");
Logger log = LoggerFactory.getLogger(sqlId);
log.info(sqlLog);
log.info(result.toString());
}
public static String beautifySql(String sql) {
sql = sql.replaceAll("[\\s\n ]+", " ");
return sql;
}
}
補充知識:Mybatis配置控制臺輸出SQL語句填充占位符
我們使用spring整合mybatis時候,希望根據(jù)控制臺輸出的sql語句來復制到Navicat等工具去測試,配置如下
在mybatis的配置文件mybatis-config.xml中配置
<configuration> <!-- | 全局配置設置 | | 可配置選項 默認值, 描述 | | aggressiveLazyLoading true, 當設置為‘true'的時候,懶加載的對象可能被任何懶屬性全部加載。否則,每個屬性都按需加載。 | multipleResultSetsEnabled true, 允許和不允許單條語句返回多個數(shù)據(jù)集(取決于驅(qū)動需求) | useColumnLabel true, 使用列標簽代替列名稱。不同的驅(qū)動器有不同的作法。參考一下驅(qū)動器文檔,或者用這兩個不同的選項進行測試一下。 | useGeneratedKeys false, 允許JDBC 生成主鍵。需要驅(qū)動器支持。如果設為了true,這個設置將強制使用被生成的主鍵,有一些驅(qū)動器不兼容不過仍然可以執(zhí)行。 | autoMappingBehavior PARTIAL, 指定MyBatis 是否并且如何來自動映射數(shù)據(jù)表字段與對象的屬性。PARTIAL將只自動映射簡單的,沒有嵌套的結果。FULL 將自動映射所有復雜的結果。 | defaultExecutorType SIMPLE, 配置和設定執(zhí)行器,SIMPLE 執(zhí)行器執(zhí)行其它語句。REUSE 執(zhí)行器可能重復使用prepared statements 語句,BATCH執(zhí)行器可以重復執(zhí)行語句和批量更新。 | defaultStatementTimeout null, 設置一個時限,以決定讓驅(qū)動器等待數(shù)據(jù)庫回應的多長時間為超時 | --> <settings> <!-- 這個配置使全局的映射器啟用或禁用緩存 --> <setting name="cacheEnabled" value="true"/> <!-- 全局啟用或禁用延遲加載。當禁用時,所有關聯(lián)對象都會即時加載 --> <setting name="lazyLoadingEnabled" value="false"/> <setting name="multipleResultSetsEnabled" value="true"/> <setting name="useColumnLabel" value="true"/> <setting name="logImpl" value="STDOUT_LOGGING" /> <setting name="defaultExecutorType" value="REUSE"/> <setting name="defaultStatementTimeout" value="25000"/> <setting name="aggressiveLazyLoading" value="true"/> </settings> </configuration>
配置上面后就可以在控制臺輸出sql語句了,但是語句與條件會分開輸出,我們想填充sql語句的占位符的話需要再spring整合mybatis中加配置

只要添加這個即可<!-- Mybatis配置控制臺輸出SQL語句填充占位符-->
<!-- 性能攔截器,兼打印sql,不生產(chǎn)環(huán)境配置 --> <bean id="performanceInterceptor" class="com.baomidou.mybatisplus.plugins.PerformanceInterceptor"> <!-- SQL 執(zhí)行最大時長,超過自動停止運行,有助于發(fā)現(xiàn)問題。 --> <property name="maxTime" value="100"></property> </bean>
以上這篇Mybatis打印替換占位符后的完整Sql教程就是小編分享給大家的全部內(nèi)容了,希望能給大家一個參考,也希望大家多多支持腳本之家。
SpringMVC五種類型參數(shù)傳遞及json傳遞參數(shù)
Java求一個分數(shù)數(shù)列的前20項之和的實現(xiàn)代碼
BufferedWriter如何使用write方法實現(xiàn)換行
Java中IO流的BufferedOutputStream和FileOutputStream對比
SpringBoot項目為何引入大量的starter?如何自定義starter?

