好用的sql攔截器
寫專案的時候遇到了好用的sql攔截器
,自記錄,整體結構如下:
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MybatisConfiguration {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor(new MybatisPlusAllSqlLog());
return mybatisPlusInterceptor;
}
}
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 org.springframework.util.CollectionUtils;
import java.sql.SQLException;
import java.text.DateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.regex.Matcher;
public class MybatisPlusAllSqlLog implements InnerInterceptor {
public static final Logger log = LoggerFactory.getLogger("sys-sql");
@Override
public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
logInfo(boundSql, ms, parameter);
}
@Override
public void beforeUpdate(Executor executor, MappedStatement ms, Object parameter) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameter);
logInfo(boundSql, ms, parameter);
}
private static void logInfo(BoundSql boundSql, MappedStatement ms, Object parameter) {
try {
log.info("parameter = " + parameter);
// 獲取到節點的id,即sql語句的id
String sqlId = ms.getId();
log.info("sqlId = " + sqlId);
// 獲取節點的配置
Configuration configuration = ms.getConfiguration();
// 獲取到最終的sql語句
String sql = getSql(configuration, boundSql, sqlId);
log.info("完整的sql:{}", sql);
} catch (Exception e) {
log.error("異常:{}", e.getLocalizedMessage(), e);
}
}
// 封裝了一下sql語句,使得結果返回完整xml路徑下的sql語句節點id + sql語句
public static String getSql(Configuration configuration, BoundSql boundSql, String sqlId) {
return sqlId + ":" + showSql(configuration, boundSql);
}
// 進行?的替換
public static String showSql(Configuration configuration, BoundSql boundSql) {
// 獲取引數
Object parameterObject = boundSql.getParameterObject();
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
// sql語句中多個空格都用一個空格代替
String sql = boundSql.getSql().replaceAll("[\\s]+", " ");
if (!CollectionUtils.isEmpty(parameterMappings) && parameterObject != null) {
// 獲取型別處理器註冊器,型別處理器的功能是進行java型別和資料庫型別的轉換
TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
// 如果根據parameterObject.getClass()可以找到對應的型別,則替換
if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
sql = sql.replaceFirst("\\?",
Matcher.quoteReplacement(getParameterValue(parameterObject)));
} else {
// MetaObject主要是封裝了originalObject物件,提供了get和set的方法用於獲取和設定originalObject的屬性值,主要支援對JavaBean、Collection、Map三種型別物件的操作
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)) {
// 該分支是動態sql
Object obj = boundSql.getAdditionalParameter(propertyName);
sql = sql.replaceFirst("\\?",
Matcher.quoteReplacement(getParameterValue(obj)));
} else {
// 列印出缺失,提醒該引數缺失並防止錯位
sql = sql.replaceFirst("\\?", "缺失");
}
}
}
}
return sql;
}
// 如果引數是String,則新增單引號, 如果是日期,則轉換為時間格式器並加單引號; 對引數是null和不是null的情況作了處理
private static String getParameterValue(Object obj) {
String value;
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(new Date()) + "'";
} else {
if (obj != null) {
value = obj.toString();
} else {
value = "";
}
}
return value;
}
}
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.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Properties;
@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, CacheKey.class, BoundSql.class})})
public class SqlStatementInterceptor implements Interceptor {
public static final Logger log = LoggerFactory.getLogger("sys-sql");
@Override
public Object intercept(Invocation invocation) throws Throwable {
long startTime = System.currentTimeMillis();
try {
return invocation.proceed();
} finally {
long timeConsuming = System.currentTimeMillis() - startTime;
log.info("執行SQL:{}ms", timeConsuming);
if (timeConsuming > 999 && timeConsuming < 5000) {
log.info("執行SQL大於1s:{}ms", timeConsuming);
} else if (timeConsuming >= 5000 && timeConsuming < 10000) {
log.info("執行SQL大於5s:{}ms", timeConsuming);
} else if (timeConsuming >= 10000) {
log.info("執行SQL大於10s:{}ms", timeConsuming);
}
}
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
}