MyBatis拦截器实现SQL打印
mybatis有自带的sql打印, 但只会出现在抛异常的时候, 或者配置日志输出, 但是输出的日志较为冗长
像这样
### Error querying database. Cause: java.lang.ArithmeticException: / by zero
### The error may exist in file [/usr/local/userMapper.xml]
### The error may involve com.xxx.xxx.UserMapper.query_COUNT
### The error occurred while handling results
### SQL: SELECT count(0) FROM (SELECT id, username FROM user) table_count
### Cause: java.lang.ArithmeticException: / by zeroat org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30) ~[mybatis-3.5.6.jar:3.5.6]
翻了下源码, 找到了这个方法, 贴一下
@Overridepublic <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {try {MappedStatement ms = configuration.getMappedStatement(statement);return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);} catch (Exception e) {throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);} finally {ErrorContext.instance().reset();}}
其中ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
顾名思义, 对异常进行包装
点进方法
public static RuntimeException wrapException(String message, Exception e) {return new PersistenceException(ErrorContext.instance().message(message).cause(e).toString(), e);
}
关键在于ErrorContext.instance()
的toString()
该方法的部分代码
// sqlif (sql != null) {description.append(LINE_SEPARATOR);description.append("### SQL: ");description.append(sql.replace('\n', ' ').replace('\r', ' ').replace('\t', ' ').trim());}
且通过ErrorContext.instance()
这个方法也能知道, 该类维护了一个ThreadLocal, 保证sql是线程安全的
于是就有了如下拦截器代码
@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}),}
)
@Log4j2
public class MybatisSqlLogInterceptor implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {Object proceed = invocation.proceed();try {Field field = ErrorContext.class.getDeclaredField("sql");field.setAccessible(true);Object sql = field.get(ErrorContext.instance());if (sql != null) {System.out.println("mybatis sql: " + sql.toString());}} catch (Exception e) {log.error("sql打印失败", e);}return proceed;}@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}@Overridepublic void setProperties(Properties properties) {}
}
执行查询, sql打印, 完成
如果想将参数也填充进去
可以这么写
@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}),}
)
@Log4j2
public class MybatisSqlLogInterceptor implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {Object proceed = invocation.proceed();// 获取xml中的一个select/update/insert/delete节点,是一条SQL语句MappedStatement mappedStatement = (MappedStatement)invocation.getArgs()[0];Object parameter = null;// 获取参数,if语句成立,表示sql语句有参数,参数格式是map形式if (invocation.getArgs().length > 1) {parameter = invocation.getArgs()[1];}// BoundSql就是封装myBatis最终产生的sql类BoundSql boundSql = mappedStatement.getBoundSql(parameter);// 获取节点的配置Configuration configuration = mappedStatement.getConfiguration();// 获取到最终的sql语句String sql = showSql(configuration, boundSql);System.out.println("mybatis sql: " + sql);return proceed;}/*** 如果参数是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;}/*** 进行?的替换** @param configuration* @param boundSql* @return*/public String showSql(Configuration configuration, BoundSql boundSql) throws NoSuchFieldException, IllegalAccessException {// 获取参数Object parameterObject = boundSql.getParameterObject();List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();// sql语句中多个空格都用一个空格代替String sql = getRealSql().replaceAll("[\\s]+", " ");if (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)) {// 该分支是动态sqlObject obj = boundSql.getAdditionalParameter(propertyName);sql = sql.replaceFirst("\\?",Matcher.quoteReplacement(getParameterValue(obj)));} else {// 打印出缺失,提醒该参数缺失并防止错位sql = sql.replaceFirst("\\?", "缺失");}}}}return sql;}/*** 获取真实sql, mybatis将其存在ThreadLocal中* @return* @throws IllegalAccessException* @throws NoSuchFieldException*/private String getRealSql() throws IllegalAccessException, NoSuchFieldException {Field field = ErrorContext.class.getDeclaredField("sql");field.setAccessible(true);Object sql = field.get(ErrorContext.instance());if (sql != null) {return sql.toString();}return "";}@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}@Overridepublic void setProperties(Properties properties) {}
}
MyBatis拦截器实现SQL打印相关推荐
- Mybatis拦截器修改sql语句
拦截器介绍 MyBatis提供了一种插件(plugin)的功能,虽然叫做插件,但其实这是拦截器功能. MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用.默认情况下,MyBatis 允许 ...
- mybatis使用拦截器显示sql,使用druid配置连接信息
mybatis使用拦截器显示sql,使用druid配置连接信息 mybatis sql Druid 1.显示出sql内容: 新建2个类: MybatisInterceptor :拦截sql,并获得输出 ...
- mybatis拦截器,并使用jsqlparser重写sql
mybatis拦截器 mybatis拦截器基础知识 编写类继承mybatis的Interceptor接口,并实现其相关方法.mybatis的拦截器,是被动态代理类主动调用的. intercept:在拦 ...
- insert into select 主键自增_springboot2结合mybatis拦截器实现主键自动生成
点击上方蓝字关注我们 1 01 前言 前阵子和朋友聊天,他说他们项目有个需求,要实现主键自动生成,不想每次新增的时候,都手动设置主键.于是我就问他,那你们数据库表设置主键自动递增不就得了.他的回答是他 ...
- Mybatis 拦截器介绍
Mybatis 拦截器介绍 1.1 目录 1.2 前言 1.3 Interceptor接口 1.4 注册拦截器 1.5 Mybatis可拦截的方法 1.6 利用拦截器进行分页 拦截器的一个作用就是我们 ...
- by mybatis 自定义order_springboot2结合mybatis拦截器实现主键自动生成
点击上方蓝字关注我们 1 01 前言 前阵子和朋友聊天,他说他们项目有个需求,要实现主键自动生成,不想每次新增的时候,都手动设置主键.于是我就问他,那你们数据库表设置主键自动递增不就得了.他的回答是他 ...
- 真正理解mybatis拦截器以及Interceptor和Plugin作用
看了很多博客文章和,mybatis 的拦截器概念还是不能很好理解, 可能是因为自己基础不好或者理解方式和他人不同吧,所以决定自己花时间好好捋捋, 然后把理解后的总结记录下来,供他人参考,也许你们的理解 ...
- 《转载》Mybatis 拦截器介绍
Mybatis 拦截器介绍 1.1 目录 1.2 前言 1.3 Interceptor接口 1.4 注册拦截器 1.5 Mybatis可拦截的方法 1.6 利用拦截器进行分页 1.2 拦截器的一个作用 ...
- Springboot 自定义mybatis 拦截器,实现我们要的扩展
前言 相信大家对拦截器并不陌生,对mybatis也不陌生. 有用过pagehelper的,那么对mybatis拦截器也不陌生了,按照使用的规则触发sql拦截,帮我们自动添加分页参数 . 那么今天,我们 ...
- Mybatis拦截器安全加解密MySQL数据实战
需求背景 公司为了通过一些金融安全指标(政策问题)和防止数据泄漏,需要对用户敏感数据进行加密,所以在公司项目中所有存储了用户信息的数据库都需要进行数据加密改造.包括Mysql.redis.mongod ...
最新文章
- 技术“摸鱼” 大神,国外小哥 5 年白拿 45 万工资!
- 控件View动态设置高度时会卡顿、速度慢的情况解决
- 如何让 zend studio 10 识别 Phalcon语法并且进行语法提示
- Spring Cloud Gateway
- 基于 FFmpeg 的播放器 demo
- jquery通过attr取html里自定义属性原来这么方便啊
- mysql触发器的要素_MySQL触发器
- 猎豹浏览器截图在哪 猎豹浏览器如何截图
- 2021-07-05-日历
- asterisk概述和代码分析
- hdu 2082 找单词(母函数)
- 【论文】PathQG: 基于事实的神经问题生成
- 理解PPAPI的设计
- Sublime Text 一键删除空白行的方法
- 学会 Python 到底能干嘛?我们整理了 7 大工作方向 + 170 个项目课程给你
- python微信接龙转Excel表格
- 芝士合集(以便查看)
- Lua:01---Lua语言介绍、运行Lua程序(lua解释器)
- 人工智能在电力系统的典型应用有哪些
- 华为那个手机是鸿蒙,EMUI 11就是鸿蒙前奏 华为手机全面升级鸿蒙OS稳了