在进行查询的时候偶尔会忘记加索引,下面根据mybatis的拦截器简单实现了一个索引检测工具,它会根据给定的sql查询出可用的索引及实际使用的索引,可以将其集成在junit里使用。记得在项目mybatis文件配置这个拦截器:
<plugins>
<plugin interceptor="test.jd.paipai.common.junit.slowSql.CheckSlowSqlInterceptor" />
</plugins>
import org.apache.commons.lang3.StringUtils;
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.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 java.sql.*;
import java.util.List;
import java.util.Properties;
/**
* @Author:weilu
* @Date: 2018/12/25 10:03
* 对执行的sql进行拦截并作索引分析处理
*/
@Intercepts({@Signature(type = Executor.class,method = "query",args={MappedStatement.class,Object.class, RowBounds.class, ResultHandler.class}),
@Signature(type=Executor.class,method = "update",args={MappedStatement.class,Object.class}) })
public class CheckSlowSqlInterceptor implements Interceptor {
private final static String DESC = "DESC ";
@Override
public Object intercept(Invocation invocation) throws Throwable {
System.out.println("intercept start----------");
MappedStatement statement = (MappedStatement)invocation.getArgs()[0];
Object parameter = null;
if(invocation.getArgs().length > 1){
parameter = invocation.getArgs()[1];
}
BoundSql boundSql = statement.getBoundSql(parameter);
Connection connection = statement.getConfiguration().getEnvironment().getDataSource().getConnection();
Configuration configuration = statement.getConfiguration();
String sql = getSql(configuration, boundSql);
System.out.println("要执行的sql:"+sql);
explainSql(connection,sql);
//showProfiles(connection);
Object returnValue = null;
returnValue = invocation.proceed();
return returnValue;
}
/**
* 获取执行的sql
* @param configuration
* @param boundSql
* @return
*/
private static String getSql(Configuration configuration, BoundSql boundSql) {
String sql = showSql(configuration, boundSql);
StringBuilder str = new StringBuilder();
str.append(sql);
return str.toString();
}
/**
* 获取执行的sql,取参数
* @param configuration
* @param boundSql
* @return
*/
private static String showSql(Configuration configuration, BoundSql boundSql) {
Object parameterObject = boundSql.getParameterObject();
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
String sql = boundSql.getSql().replaceAll("[\\s]+", " ");
if (parameterMappings.size() > 0 && parameterObject != null) {
TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
sql = sql.replaceFirst("\\?", 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("\\?", getParameterValue(obj));
} else if (boundSql.hasAdditionalParameter(propertyName)) {
Object obj = boundSql.getAdditionalParameter(propertyName);
sql = sql.replaceFirst("\\?", getParameterValue(obj));
}
}
}
}
return sql;
}
/**
* 获取参数信息
* @param obj
* @return
*/
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(new Date(System.currentTimeMillis())) + "'";
} else {
if (obj != null) {
value = obj.toString();
} else {
value = "";
}
}
return value;
}
/**
* 获取执行sql的索引
* @param connection
* @param sql
*/
private void explainSql(Connection connection,String sql){
try {
Statement statement = connection.createStatement();
String desc_sql = DESC + sql;
ResultSet result = statement.executeQuery(desc_sql);
if(result != null && result.next()){
String possibleKey = result.getString("possible_keys");
String key = result.getString("key");
String type = result.getString("type");
String extra = result.getString("extra");
System.out.println("可以用到的索引:"+possibleKey);
System.out.println("实际使用的索引:"+key);
System.out.println("extra信息:"+extra);
System.out.println("type信息:"+type);
if(StringUtils.isNotBlank(possibleKey)){
if(StringUtils.isBlank(key)){
System.out.println("未使用的索引:"+possibleKey);
}else{
System.out.println("已使用索引:"+key);
}
}else{
System.out.println("没可使用的索引");
}
}
} catch (SQLException e) {
e.printStackTrace();
}
}
private void showProfiles(Connection connection){
String profile = "SHOW PROFILES";
try {
Statement statement = connection.createStatement();
ResultSet result = statement.executeQuery(profile);
if(result != null){
String Query_ID = result.getString("Query_ID");
String Duration = result.getString("Duration");
String Query = result.getString("Query");
System.out.println("Query_ID:"+Query_ID);
System.out.println("Duration:"+Duration);
System.out.println("Query:"+Query);
}
} catch (SQLException e) {
e.printStackTrace();
}
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
}