mybatis插件-sql日志打印(sql格式化方式)

  作者:记性不好的阁主

拦截器代码:


import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Pair;
import com.ucmed.ibh.common.util.LogUtils;
import com.ucmed.ibh.common.util.RegexUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.scripting.xmltags.ForEachSqlNode;
import tk.mybatis.mapper.entity.Example;

import java.sql.Connection;
import java.util.*;


/**
* 日志拦截器
*/
@Intercepts(
@Signature(
type = StatementHandler.class,
method = "prepare",
args = {Connection.class, Integer.class}))
public class SqlLogInterceptor implements Interceptor {

// 是否开启
public static boolean enable;
// 是否美化
public static boolean beautify;

public static final ThreadLocal<Set<String>> dupSqlSetThreadLocal = ThreadLocal.withInitial(HashSet::new);

@Override
public Object intercept(Invocation invocation) throws Throwable {
if(enable) {
String preSql = "";
String sql = "";
List<String> params = new ArrayList<>();
Object parameterObject = null;
try {
StatementHandler target = (StatementHandler) invocation.getTarget();
// Sql sql,参数,参数映射
BoundSql boundSql = target.getBoundSql();
// 如果有%号,则转义%号,配合String.format
preSql = escapePercent(boundSql.getSql());
// 过滤重复sql
Set<String> dupSqlSet = dupSqlSetThreadLocal.get();
if(dupSqlSet.contains(preSql)) {
return invocation.proceed();
}
dupSqlSet.add(preSql);

List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
parameterObject = boundSql.getParameterObject();
String sqlSegmentPattern = preSql.replaceAll("\\?", "%s");
int placeholderSize = countStr(preSql, "?");
if(null != parameterObject) {
// 如果是Example (现只支持简单查询 where param1 = value1 and param2 = value2 ...)
if(parameterObject instanceof Example) {
Example example = (Example) parameterObject;
List<Example.Criteria> oreCriteriaList = example.getOredCriteria();
if(CollectionUtil.isNotEmpty(oreCriteriaList)){
for (int i = 0; i < oreCriteriaList.size(); i++) {
Example.Criteria criteria = oreCriteriaList.get(i);
List<Example.Criterion> criteriaList = criteria.getCriteria();
if(CollectionUtil.isNotEmpty(criteriaList)){
for (int j = 0; j < placeholderSize; j++) {
Example.Criterion criterion = criteriaList.get(j);
String value = criterion.getValue() + "" ;
if(!RegexUtils.isNumber(value)) {
value = "\"" + value + "\"";
}
params.add(value);
}
}
}
sql = String.format(sqlSegmentPattern, params.toArray());
}

} else {
// 如果不是Example
// System.out.println(parameterMappings);
// System.out.println(parameterObject);
List<Pair<String, Pair<String, String>>> paramMapperList = transform(parameterObject.toString());
// List<String> params = new ArrayList<>();

for (int i = 0; i < parameterMappings.size(); i++) {
String property = parameterMappings.get(i).getProperty();
for (int j = 0; j < paramMapperList.size(); j++) {
String key = paramMapperList.get(j).getKey();
if(property.equals(key)) {
String value = paramMapperList.get(j).getValue().getValue();
if(!RegexUtils.isNumber(value)) {
value = "\"" + value + "\"";
}
params.add(value);
break;
}
}
}

if(placeholderSize == 1) {
// select * from table where id = ?
// 此时segmentsSize==1
// select * from table where id = ? order by create_time
// 此时segmentsSize>1
if(CollectionUtil.isEmpty(params)) {
String value = parameterObject.toString();
if(!(value.startsWith("{") && value.endsWith("}"))) {
if(!RegexUtils.isNumber(value)) {
value = "\"" + value + "\"";
}
sql = String.format(sqlSegmentPattern, value);
}
}
} else {
if(CollectionUtil.isNotEmpty(params)) {
sql = String.format(sqlSegmentPattern, params.toArray());
}
}
// System.out.println(params);
// System.out.println(sqlBuilder);
// System.out.println(beautifySql(sqlBuilder.toString()));
}
// 还原%
sql = reductionPercent(sql);
// 打印sql
outSql(sql);
} else {
LogUtils.outSql(preSql);
}
} catch (Exception e) {
LogUtils.error("打印日志出错:");
LogUtils.error("parameterObject" + parameterObject);
LogUtils.error("preSql\n" + preSql);
LogUtils.error("params" + params);
LogUtils.error(e);
}
}
return invocation.proceed();
}

@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}

@Override
public void setProperties(Properties properties) {

}

private static List<Pair<String, Pair<String, String>>> transform(String source) {
if(source.startsWith("{") && source.endsWith("}")) {
source = source.substring(1, source.length()-1);
source = source + ", ";
}
char[] chars = source.toCharArray();
StringBuilder sb = new StringBuilder();
StringBuilder objectParamBuilder = new StringBuilder();
List<Pair<String, Pair<String, String>>> paramMapperList = new ArrayList<>();
boolean isList = false;
boolean isObject = false;
// charArray数组下标
int index = 0;
// sql入参名称key
String key = "";
// 列表key
String listKey = "";
// sql参数值
String value = "";
// list的下标
int listIndex = 0;
// Object属性的下标
int objectIndex = 0;
for (char aChar : chars) {

if('{' == aChar) {
sb.setLength(0);
isObject = true;
}
if(isObject) {
index++;
if('}' == aChar) {
isObject = false;
}
if(RegexUtils.isParam(key)) {
continue;
}
if('}' != aChar) {
if('\'' == aChar) {
continue;
}
if('=' == aChar) {
objectParamBuilder.insert(objectParamBuilder.length()-(objectIndex-1), key + ".");
} else {
if(',' == aChar) {
objectIndex = 0;
} else {
objectIndex++;
}
}
objectParamBuilder.append(aChar);
continue;
}
List<Pair<String, Pair<String, String>>> objectParamMapperList = transform(objectParamBuilder.append("}").toString());
sb.setLength(0);
// System.out.println(objectParamMapperList);
objectParamBuilder.setLength(0);
paramMapperList.addAll(objectParamMapperList);
isObject = false;
}
objectIndex = 0;

if('=' == aChar && chars[index+1] == '[') {
// list
key = sb.toString();
isList = true;
sb.setLength(0);
}
if('=' == aChar && chars[index+1] != '[') {
key = sb.toString();
sb.setLength(0);
}
if('=' != aChar && ' ' != aChar && ',' != aChar && !isList && '}' != aChar) {
sb.append(aChar);
}
if('=' != aChar && ' ' != aChar && isList && '[' != aChar && ']' != aChar) {
if(',' == aChar) {

value = sb.toString();
sb.setLength(0);
if(RegexUtils.isParam(key)) {
continue;
}
Pair<String, String> pair = new Pair<>("list", value);
listKey = ForEachSqlNode.ITEM_PREFIX + key + "_" + listIndex;
Pair<String, Pair<String, String>> mapper = new Pair<>(listKey, pair);
paramMapperList.add(mapper);
listIndex++;

} else {
sb.append(aChar);
}
}

if((',' == aChar && !isList && ']' != chars[index-1])) {
value = sb.toString();
sb.setLength(0);
if(RegexUtils.isParam(key) || "".equals(value)) {
continue;
}

Pair<String, String> pair = new Pair<>("string", value);
Pair<String, Pair<String, String>> mapper = new Pair<>(key, pair);
paramMapperList.add(mapper);
}

if(']' == aChar && isList) {
value = sb.toString();
sb.setLength(0);
if(RegexUtils.isParam(key)) {
isList = false;
continue;
}
Pair<String, String> pair = new Pair<>("list", value);
listKey = ForEachSqlNode.ITEM_PREFIX + key + "_" + listIndex;
Pair<String, Pair<String, String>> mapper = new Pair<>(listKey, pair);
paramMapperList.add(mapper);
isList = false;
listIndex=0;
}

index++;

}
return paramMapperList;
}

/**
* 美化Sql
*/
private String beautifySql(String sql) {
sql = sql.replaceAll("[\\s\n ]+"," ");
return sql;
}

/**
* 输出Sql
*/
private void outSql(String sql) {
if(StringUtils.isNotBlank(sql)) {
if(beautify) {
LogUtils.outSql(beautifySql(sql));
} else {
LogUtils.outSql(sql);
}
}
}

/**
* 重置重复sql存储集合
*/
public static void resetDupSqlSet() {
if(enable) {
Set<String> dupSqlSet = dupSqlSetThreadLocal.get();
dupSqlSet.clear();
dupSqlSetThreadLocal.set(dupSqlSet);
}
}

/**
* 判断字符串中某个字符存在的个数
* @param text 完整文本
* @param str 要统计匹配个数的字符
* @return 要统计匹配字符的个数
*/
public static int countStr(String text, String str) {
int count=0;
if (!text.contains(str)) {
return 0;
}
while(text.contains(str)){
count++;
text=text.substring(text.indexOf(str)+str.length());
}
return count;
}

/**
* 转义%
* @param text 待转义文本
* @return 转义后文本
*/
private String escapePercent(String text) {
return text.contains("%") ? text.replaceAll("%", "%%") : text;
}

/**
* 还原%
* @param text 待还原文本
* @return 还原后文本
*/
private String reductionPercent(String text) {
return text.contains("%%") ? text.replaceAll("%%", "%") : text;
}




}


LogUtils.java


import lombok.extern.slf4j.Slf4j;

import java.io.PrintWriter;
import java.io.StringWriter;

@Slf4j
public class LogUtils {

public static void outSql(String sql) {
log.info("======================================================?=============");
log.info("执行sql语句:\n" + sql);
log.info("======================================================!=============");
}

public static void error(String errorMsg){
log.error(errorMsg);
}

public static void error(Throwable e){
if(e!=null){
StringWriter trace = new StringWriter();
e.printStackTrace(new PrintWriter(trace));
log.error(trace.toString());
}
}

}


RegexUtils.java


import org.apache.commons.lang.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegexUtils {

private static final Pattern NUMBER = Pattern
.compile("\\d+");

private static final Pattern PARAM = Pattern
.compile("param\\d+");

/**
* 获取表情索引数字
* @param regex
* @param source
* @return
*/
public static List<String> findAll(String regex, String source, int group_id) {

Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(source);
List<String> list = new ArrayList<>();
while (matcher.find()) {
list.add(matcher.group(group_id));
}
return list;

}

/**
* 判断是否是数字.
*
* @param str 参数字符串.
* @return true 判断是数字;false 判断不是数字。
*/
public static boolean isNumber(String str) {
if (StringUtils.isBlank(str)) {
return false;
}
Matcher m = NUMBER.matcher(str);
return m.matches();
}

/**
* 判断是否是参数.
*
* @param param 参数字符串.
* @return true 判断是参数;false 判断不是参数。
*/
public static boolean isParam(String param) {
if (StringUtils.isBlank(param)) {
return false;
}
Matcher m = PARAM.matcher(param);
return m.matches();
}


}


根据参数直接打印真正的sql语句


相关推荐

评论 抢沙发

表情

分类选择