mybatis-sql拦截器打印sql执行时间
形而上 Lv4

集成mybatis,打印sql以及执行时间~

mybatis拦截器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
import lombok.extern.slf4j.Slf4j;
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.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.springframework.util.CollectionUtils;

import java.text.DateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import java.util.regex.Matcher;

/**
* @Author
* @Date 2020/7/29 14:19
* @Version 版本号
* @Description mybatis执行sql耗时
*/

@Slf4j
@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}
), @Signature(
type = Executor.class,
method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}
)})
public class SqlStatementInterceptor implements Interceptor {

@Override
public Object intercept(Invocation invocation) throws Throwable {
long startTime = System.currentTimeMillis();
try {
return invocation.proceed();
} finally {
long timeConsuming = System.currentTimeMillis() - startTime;
int level;

if (timeConsuming < 100) {
level = 0;
} else if (timeConsuming < 200) {
level = 1;
} else if (timeConsuming < 300) {
level = 2;
} else if (timeConsuming < 400) {
level = 3;
} else if (timeConsuming < 500) {
level = 4;
} else {
level = 5;
}
Object[] args = invocation.getArgs();
MappedStatement ms = (MappedStatement) args[0];
Object parameter = null;
//获取参数,if语句成立,表示sql语句有参数,参数格式是map形式
if (invocation.getArgs().length > 1) {
parameter = invocation.getArgs()[1];
}
String sqlId = ms.getId();// 获取到节点的id,即sql语句的id
BoundSql boundSql = ms.getBoundSql(parameter); // BoundSql就是封装myBatis最终产生的sql类
Configuration configuration = ms.getConfiguration(); // 获取节点的配置
String sql = showSql(configuration, boundSql); // 获取到最终的sql语句

log.info("【执行SQL】: {}level {}ms {}", level, timeConsuming, sql);
}
}

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

@Override
public void setProperties(Properties properties) {

}

private 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类型和数据库类型的转换     // 如果根据parameterObject.getClass()可以找到对应的类型,则替换
TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(parameterObject)));

} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
// MetaObject主要是封装了originalObject对象,提供了get和set的方法用于获取和设置originalObject的属性值,
// 主要支持对JavaBean、Collection、Map三种类型对象的操作
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 = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(obj)));
} else {
sql = sql.replaceFirst("\\?", "缺失");
}//打印出缺失,提醒该参数缺失并防止错位
}
}
}
return sql;
}

/**
* 如果参数是String,则添加单引号, 如果是日期,则转换为时间格式器并加单引号;
* 对参数是null和不是null的情况作了处理
*/
private static String getParameterValue(Object obj) {
String value = null;
if (obj instanceof String) {
value = "'" + obj.toString() + "'";
} else if (obj instanceof Date) {
Date date = (Date) obj;
DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.CHINA);
value = "'" + formatter.format(date) + "'";
} else {
if (obj != null) {
value = obj.toString();
} else {
value = "";
}

}
return value;
}
}

配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import javax.sql.DataSource;

@Configuration
@MapperScan(basePackages = "com.xx.staytime.mapper",sqlSessionFactoryRef = "xxDatasourceSessionFactory")
public class SxFactoryDatasourceConfig {

@Bean(name = "xxDataSource")
@ConfigurationProperties(prefix = "xx.datasource")
public DataSource dataSource(){
return DruidDataSourceBuilder.create().build();
}

@Bean(name = "xxDatasourceSessionFactory")
public SqlSessionFactory sqlSessionFactory(MybatisPlusInterceptor mybatisPlusInterceptor,SqlStatementInterceptor sqlStatementInterceptor) throws Exception {
MybatisSqlSessionFactoryBean sessionFactoryBean = new MybatisSqlSessionFactoryBean();
sessionFactoryBean.setDataSource(dataSource());
sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
Interceptor[] plugins = {mybatisPlusInterceptor,sqlStatementInterceptor};
sessionFactoryBean.setPlugins(plugins);
return sessionFactoryBean.getObject();
}
}

eg.

1
2
3
4
5
16:40:00.119 app [scheduling-1] INFO  com.x.conf.SqlStatementInterceptor - 【执行SQL】: 0level 2ms SELECT id,cross_record_syscode,entrance_name,vehicle_out,release_mode,cross_time,plate_no,responsible_user_name,responsible_user_tel,order_type,scr_id,wv_id,delete_status,updater FROM trucks_cross_records WHERE (cross_record_syscode = '5879c4b1746343daa49bceeaec583de2_5c8df9aeea91c_17d') 
16:40:00.121 app [scheduling-1] INFO com.x.conf.SqlStatementInterceptor - 【执行SQL】: 0level 2ms SELECT id,cross_record_syscode,entrance_name,vehicle_out,release_mode,cross_time,plate_no,responsible_user_name,responsible_user_tel,order_type,scr_id,wv_id,delete_status,updater FROM trucks_cross_records WHERE (cross_record_syscode = '5879c4b1746343daa49bceeaec583de2_5c8df9aeea91c_17d')
16:40:00.124 app [scheduling-1] INFO com.x.conf.SqlStatementInterceptor - 【执行SQL】: 0level 1ms SELECT id,cross_record_syscode,entrance_name,vehicle_out,release_mode,cross_time,plate_no,responsible_user_name,responsible_user_tel,order_type,scr_id,wv_id,delete_status,updater FROM trucks_cross_records WHERE (cross_record_syscode = '5879c4b1746343daa49bceeaec583de2_5c8df9aeea91c_17d')
16:40:00.126 app [scheduling-1] INFO com.x.conf.SqlStatementInterceptor - 【执行SQL】: 0level 2ms SELECT id,cross_record_syscode,entrance_name,vehicle_out,release_mode,cross_time,plate_no,responsible_user_name,responsible_user_tel,order_type,scr_id,wv_id,delete_status,updater FROM trucks_cross_records WHERE (cross_record_syscode = '5879c4b1746343daa49bceeaec583de2_5c8df9aeea91c_17d')
16:40:00.128 app [scheduling-1] INFO com.x.conf.SqlStatementInterceptor - 【执行SQL】: 0level 2ms SELECT id,cross_record_syscode,entrance_name,vehicle_out,release_mode,cross_time,plate_no,responsible_user_name,responsible_user_tel,order_type,scr_id,wv_id,delete_status,updater FROM trucks_cross_records WHERE (cross_record_syscode = '5879c4b1746343daa49bceeaec583de2_5c8df9aeea91c_17d')
  • 本文标题:mybatis-sql拦截器打印sql执行时间
  • 本文作者:形而上
  • 创建时间:2021-08-06 12:45:30
  • 本文链接:https://deepter.gitee.io/2021_08_06_mybatis-sql拦截器打印sql执行时间/
  • 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!