您当前的位置:首页 > 电脑百科 > 数据库 > MYSQL

换种方式,打印 MyBatis 执行 SQL 语句!

时间:2023-01-29 16:31:48  来源:今日头条  作者:小傅哥

作者:小傅哥
博客:https://bugstack.cn

 

沉淀、分享、成长,让自己和他人都能有所收获!
一、前言

 

片面了!

一月三舟,托尔斯泰说:“多么伟大的作家,也不过就是在书写自己的片面而已”。何况是我,何况是我们!

虽然我们不书写文章,但我们写需求、写代码、写注释,当我们遇到了需要被讨论的问题点时,往往变成了争论点。这个好、那个差、你用的都是啥啥啥!

当你把路走窄了,你所能接受到的新的思路、新的想法、新的视野,以及非常重要的收入,也都会随之减少。只有横向对比、参考借鉴、查漏补缺,才能让你的头脑中会有更多的思路,无论是在写代码上、还是在理财上、还是在生活上。

二、需求目的

你是否有在使用 IntelliJ IDEA 做开发的过程,需要拿到执行 SQL 语句,复制出来做验证的时候,总是这样的语句:SELECT * FROM USER WHERE id = ? AND name = ? 又需要自己把 ? 号 替换成入参值呢?

当然这个需求其实并不大,甚至你还可以使用其他方式解决。那么在本章节会给你提供一个新的思路,可能你几乎是没过的方式进行处理。

那么在这个章节的案例中我们用到基于 IDEA Plugin 开发能力,把字节码插桩探针,基于 JAVAagent 的能力,注入到代码中。再通过增强后的字节码,获取到 com.MySQL.jdbc.Preparedstatement -> executeInternal 执行时的对象,从而拿到可以直接测试的 SQL 语句。

三、案例开发 1. 工程结构 guide-idea-plugin-probe ├── .gradle ├── probe-agent │ ├── src │ │ └── mAIn │ │ └── java │ │ └── cn.bugstack.guide.IDEA.plugin │ │ ├── MonitorMethod.java │ │ └── PreAgent.java │ └── build.gradle └── probe-plugin │ └── src │ │ └── main │ │ ├── Java │ │ │ └── cn.bugstack.guide.idea.plugin │ │ │ └── utils │ │ │ │ └── PluginUtil.java │ │ │ └── PerRun.java │ │ └── resources │ │ └── META-INF │ │ └── plugin.xml │ └── build.gradle ├── build.gradle └── gradle.properties

公众号:bugstack虫洞栈 回复:idea 即可下载全部 IDEA 插件开发源码

在此 IDEA 插件工程中,工程结构分为2块:

 

  • probe-agent:探针模块,用于编译打包提供字节码增强服务,给 probe-plugin 模块使用
  • probe-plugin:插件模块,通过 java.programPatcher 加载字节码增强包,获取并打印执行数据库操作的 SQL 语句。
2. 字节码增强获取 SQL

 

此处的字节码增强方式,采用的 Byte-Buddy 字节码框架,它的使用方式更加简单,在使用的过程中有些像使用 AOP 的拦截方式一样,获取到你需要的信息。

此外在 gradle 打包构建的时候,需要添加 shadowJar 模块,把 Premain-Class 打包进去。这部分代码中可以查看

2.1 探针入口

cn.bugstack.guide.idea.plugin.PreAgent

//JVM 首先尝试在代理类上调用以下方法 public static void premain(String agentArgs, Instrumentation inst) { AgentBuilder.Transformer transformer = (builder, typeDescription, classLoader, javaModule) -> { return builder .method(ElementMatchers.named("executeInternal")) // 拦截任意方法 .intercept(MethodDelegation.to(MonitorMethod.class)); // 委托 }; new AgentBuilder .Default() .type(ElementMatchers.nameStartsWith("com.mysql.jdbc.PreparedStatement")) .transform(transformer) .installOn(inst); }

  • 通过 Byte-buddy 配置,拦截匹配的类和方法,因为这个类和方法下,可以获取到完整的执行 SQL 语句。
2.2 拦截 SQL

 

cn.bugstack.guide.idea.plugin.MonitorMethod

@RuntimeType public static Object intercept(@This Object obj, @Origin Method method, @SuperCall Callable callable, @AllArguments Object... args) throws Exception { try { return callable.call(); } finally { String originalSql = (String) BeanUtil.getFieldValue(obj, "originalSql"); String replaceSql = ReflectUtil.invoke(obj, "asSql"); System.out.println("数据库名称:mysql"); System.out.println("线程ID:" + Thread.currentThread().getId()); System.out.println("时间:" + new Date()); System.out.println("原始SQL:rn" + originalSql); System.out.println("替换SQL:rn" + replaceSql); } }

  • 拦截方法入参是一种可配置操作,比如 @This Object obj 是为了获取当前类的执行对象,@Origin Method method 是为了获取执行方法。
  • 在 finally 块中,我们可以通过反射拿到当前类的属性信息,以及反射拿到执行的 SQL,并做打印输出。
2.3 编译打包

 

在测试和开发 IDEA Plugin 插件之前,我们需要先进行一个打包操作,这个打包就是把字节码增强的代码打包整一个 Jar 包。在 build.gradle -> shadowJar


 

 

  • 打包编译后,就可以在 build -> libs 下看到 jar:probe-agent-1.0-SNAPSHOT-all.jar 这个 Jar 就是用来做字节码增强处理的。
2.4 测试验证

 

这里在把写好的字节码增强组件给插件使用之前,可以做一个测试验证,避免每次都需要启动插件才能做测试。

单元测试

public class ApiTest { public static void main(String[] args) throws Exception { String URL = "jdbc:mysql://127.0.0.1:3306/itstack?characterEncoding=utf-8"; String USER = "root"; String PASSword = "123456"; Class.forName("com.mysql.jdbc.Driver"); Connection conn = DriverManager.getConnection(URL, USER, PASSWORD); String sql="SELECT * FROM USER WHERE id = ? AND name = ?"; PreparedStatement statement = conn.prepareStatement(sql); statement.setLong(1,1L); statement.setString(2,"谢飞机"); ResultSet rs = statement.executeQuery(); while (rs.next()) { System.out.println(rs.getString("name") + " " + rs.getString("address")); } } }

  • VM options:-javaagent:你的路径libsprobe-agent-1.0-SNAPSHOT-all.jar
  • 注意在测试运行的时候,你要给 ApiTest 配置 VM options 才能打印拦截 SQL 信息

 

测试结果

原始SQL: SELECT * FROM USER WHERE id = ? AND name = ? 替换SQL: SELECT * FROM USER WHERE id = 1 AND name = '谢飞机' 谢飞机 北京.大兴区.通明湖公园

  • 好啦,这样我们就可以拦截可以复制执行的 SQL 语句了,接下来我们再做下 IDEA Plugin 的处理。
3. 通过插件开发引入探针 Jar

 

接下来我们要把开发好的字节码增强 Jar 包,复制到 IDEA Plugin 插件开发模块中的 libs(可自己创建) 下,之后在 plugin.xml 配置加载 implementation fileTree(dir: 'libs', includes: ['*jar']) 这样就可以程序中,找到这个 jar 包并配置到程序中。

3.1 复制 jar 到 libs 下


 

3.2 build.gradle 配置加载 dependencies { implementation fileTree(dir: 'libs', includes: ['*jar']) }

  • 通过 implementation fileTree 引入加载文件树的方式,把我们配置好的 Jar 加载到程序运行中。
3.3 程序中引入 javaagent

 

cn.bugstack.guide.idea.plugin.PerRun

public class PerRun extends JavaProgramPatcher { @Override public void patchJavaParameters(Executor executor, RunProfile configuration, JavaParameters javaParameters) { RunConfiguration runconfiguration = (RunConfiguration) configuration; ParametersList vmParametersList = javaParameters.getVMParametersList(); vmParametersList.addParametersString("-javaagent:" + agentCoreJarPath); vmParametersList.addNotEmptyProperty("guide-idea-plugin-probe.projectId", runConfiguration.getProject().getLocationHash()); } }

  • 通过继承 JavaProgramPatcher 类,实现 patchJavaParameters 方法,通过 configuration 属性来配置我们自己需要被加载的 -javaagent 包。
  • 这样在通过 IDEA 已经安装此插件,运行代码的时候,就会执行到这个拦截和打印 SQL 的功能。
3.4 plugin.xml 添加配置
  • 之后你还需要把开发好的加载类,配置到 java.programPatcher 这样就可以程序运行的时候,被加载到了。
四、测试验证
  • 准备好一个有数据库操作的工程,需要的是 JDBC,如果是其他的,你需要自己扩展
  • 启动插件后,打开你的工程,运行单元测试,查看打印区

 

启动插件


 

 

  • 如果你是新下载代码,那么可以在 probe-plugin -> Tasks -> intellij -> runIde 中进行运行启动。

 

单元测试

@Test public void test_update(){ User user = new User(); user.setId(1L); user.setName("谢飞机"); user.setAge(18); user.setAddress("北京.大兴区.亦庄经济开发区"); userDao.update(user); }

测试结果

22:30:55.593 [main] DEBUG cn.bugstack.test.demo.infrastructure.dao.UserDao.update[143] - ==> Preparing: UPDATE user SET name=?,age=?,address=? WHERE id=? 22:30:55.625 [main] DEBUG cn.bugstack.test.demo.infrastructure.dao.UserDao.update[143] - ==> Parameters: 谢飞机(String), 18(Integer), 北京.大兴区.亦庄经济开发区(String), 1(Long) 数据库名称:Mysql 线程ID:1 原始SQL: UPDATE user SET name=?,age=?,address=? WHERE id=? 替换SQL: UPDATE user SET name='谢飞机',age=18,address='北京.大兴区.亦庄经济开发区' WHERE id=1

  • 通过测试结果可以看到,我们可以获取到直接拿去测试验证的 SQL 语句了,就不用在复制带问号的 SQL 还得修改后测试了。
五、总结
  • 首先我们是在本章节初步尝试使用多模块的方式来创建工程,这样的方式可以更加好维护各类一个工程下所需要的代码模块。你也可以尝试使用 gradle 创建多模块工程
  • 对于字节码插桩增强的使用方式,本篇只是一个介绍,这项技术还可以运用到更多的场景,开发出各种提升研发效率的工具。
  • 了解额外的 Jar 包是怎么加载到工程的,以及如何通过配置的方式让 javaagent 引入自己开发好的探针组件。


Tags:SQL   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
MySQL 核心模块揭秘
server 层会创建一个 SAVEPOINT 对象,用于存放 savepoint 信息。binlog 会把 binlog offset 写入 server 层为它分配的一块 8 字节的内存里。 InnoDB 会维护自己的 savepoint...【详细内容】
2024-04-03  Search: SQL  点击:(7)  评论:(0)  加入收藏
原来 SQL 函数是可以内联的!
介绍在某些情况下,SQL 函数(即指定LANGUAGE SQL)会将其函数体内联到调用它的查询中,而不是直接调用。这可以带来显著的性能提升,因为函数体可以暴露给调用查询的规划器,从而规划器...【详细内容】
2024-04-03  Search: SQL  点击:(5)  评论:(0)  加入收藏
如何正确选择NoSQL数据库
译者 | 陈峻审校 | 重楼Allied Market Research最近发布的一份报告指出,业界对于NoSQL数据库的需求正在持续上升。2022年,全球NoSQL市场的销售额已达73亿美元,预计到2032年将达...【详细内容】
2024-03-28  Search: SQL  点击:(18)  评论:(0)  加入收藏
MySQL 核心模块揭秘,你看明白了吗?
为了提升分配 undo 段的效率,事务提交过程中,InnoDB 会缓存一些 undo 段。只要同时满足两个条件,insert undo 段或 update undo 段就能被缓存。1. 关于缓存 undo 段为了提升分...【详细内容】
2024-03-27  Search: SQL  点击:(14)  评论:(0)  加入收藏
MySQL:BUG导致DDL语句无谓的索引重建
对于5.7.23之前的版本在评估类似DDL操作的时候需要谨慎,可能评估为瞬间操作,但是实际上线的时候跑了很久,这个就容易导致超过维护窗口,甚至更大的故障。一、问题模拟使用5.7.22...【详细内容】
2024-03-26  Search: SQL  点击:(13)  评论:(0)  加入收藏
从 MySQL 到 ByteHouse,抖音精准推荐存储架构重构解读
ByteHouse是一款OLAP引擎,具备查询效率高的特点,在硬件需求上相对较低,且具有良好的水平扩展性,如果数据量进一步增长,可以通过增加服务器数量来提升处理能力。本文将从兴趣圈层...【详细内容】
2024-03-22  Search: SQL  点击:(28)  评论:(0)  加入收藏
在 SQL 中写了 in 和 not in,技术总监说要炒了我……
WHY?IN 和 NOT IN 是比较常用的关键字,为什么要尽量避免呢?1、效率低项目中遇到这么个情况:t1表 和 t2表 都是150w条数据,600M的样子,都不算大。但是这样一句查询 ↓select *...【详细内容】
2024-03-18  Search: SQL  点击:(8)  评论:(0)  加入收藏
应对慢SQL的致胜法宝:7大实例剖析+优化原则
大促备战,最大的隐患项之一就是慢SQL,对于服务平稳运行带来的破坏性最大,也是日常工作中经常带来整个应用抖动的最大隐患,在日常开发中如何避免出现慢SQL,出现了慢SQL应该按照什...【详细内容】
2024-03-14  Search: SQL  点击:(10)  评论:(0)  加入收藏
MySQL自增主键一定是连续的吗?
测试环境:MySQL版本:8.0数据库表:T (主键id,唯一索引c,普通字段d)如果你的业务设计依赖于自增主键的连续性,这个设计假设自增主键是连续的。但实际上,这样的假设是错的,因为自增主键不...【详细内容】
2024-03-10  Search: SQL  点击:(12)  评论:(0)  加入收藏
准线上事故之MySQL优化器索引选错
1 背景最近组里来了许多新的小伙伴,大家在一起聊聊技术,有小兄弟提到了MySQL的优化器的内部策略,想起了之前在公司出现的一个线上问题,今天借着这个机会,在这里分享下过程和结论...【详细内容】
2024-03-07  Search: SQL  点击:(31)  评论:(0)  加入收藏
▌简易百科推荐
MySQL 核心模块揭秘
server 层会创建一个 SAVEPOINT 对象,用于存放 savepoint 信息。binlog 会把 binlog offset 写入 server 层为它分配的一块 8 字节的内存里。 InnoDB 会维护自己的 savepoint...【详细内容】
2024-04-03  爱可生开源社区    Tags:MySQL   点击:(7)  评论:(0)  加入收藏
MySQL 核心模块揭秘,你看明白了吗?
为了提升分配 undo 段的效率,事务提交过程中,InnoDB 会缓存一些 undo 段。只要同时满足两个条件,insert undo 段或 update undo 段就能被缓存。1. 关于缓存 undo 段为了提升分...【详细内容】
2024-03-27  爱可生开源社区  微信公众号  Tags:MySQL   点击:(14)  评论:(0)  加入收藏
MySQL:BUG导致DDL语句无谓的索引重建
对于5.7.23之前的版本在评估类似DDL操作的时候需要谨慎,可能评估为瞬间操作,但是实际上线的时候跑了很久,这个就容易导致超过维护窗口,甚至更大的故障。一、问题模拟使用5.7.22...【详细内容】
2024-03-26  MySQL学习  微信公众号  Tags:MySQL   点击:(13)  评论:(0)  加入收藏
从 MySQL 到 ByteHouse,抖音精准推荐存储架构重构解读
ByteHouse是一款OLAP引擎,具备查询效率高的特点,在硬件需求上相对较低,且具有良好的水平扩展性,如果数据量进一步增长,可以通过增加服务器数量来提升处理能力。本文将从兴趣圈层...【详细内容】
2024-03-22  字节跳动技术团队    Tags:ByteHouse   点击:(28)  评论:(0)  加入收藏
MySQL自增主键一定是连续的吗?
测试环境:MySQL版本:8.0数据库表:T (主键id,唯一索引c,普通字段d)如果你的业务设计依赖于自增主键的连续性,这个设计假设自增主键是连续的。但实际上,这样的假设是错的,因为自增主键不...【详细内容】
2024-03-10    dbaplus社群  Tags:MySQL   点击:(12)  评论:(0)  加入收藏
准线上事故之MySQL优化器索引选错
1 背景最近组里来了许多新的小伙伴,大家在一起聊聊技术,有小兄弟提到了MySQL的优化器的内部策略,想起了之前在公司出现的一个线上问题,今天借着这个机会,在这里分享下过程和结论...【详细内容】
2024-03-07  转转技术  微信公众号  Tags:MySQL   点击:(31)  评论:(0)  加入收藏
MySQL数据恢复,你会吗?
今天分享一下binlog2sql,它是一款比较常用的数据恢复工具,可以通过它从MySQL binlog解析出你要的SQL,并根据不同选项,可以得到原始SQL、回滚SQL、去除主键的INSERT SQL等。主要...【详细内容】
2024-02-22  数据库干货铺  微信公众号  Tags:MySQL   点击:(53)  评论:(0)  加入收藏
如何在MySQL中实现数据的版本管理和回滚操作?
实现数据的版本管理和回滚操作在MySQL中可以通过以下几种方式实现,包括使用事务、备份恢复、日志和版本控制工具等。下面将详细介绍这些方法。1.使用事务:MySQL支持事务操作,可...【详细内容】
2024-02-20  编程技术汇    Tags:MySQL   点击:(54)  评论:(0)  加入收藏
MySQL数据库如何生成分组排序的序号
经常进行数据分析的小伙伴经常会需要生成序号或进行数据分组排序并生成序号。在MySQL8.0中可以使用窗口函数来实现,可以参考历史文章有了这些函数,统计分析事半功倍进行了解。...【详细内容】
2024-01-30  数据库干货铺  微信公众号  Tags:MySQL   点击:(55)  评论:(0)  加入收藏
mysql索引失效的场景
MySQL中索引失效是指数据库查询时无法有效利用索引,这可能导致查询性能显著下降。以下是一些常见的MySQL索引失效的场景:1.使用非前导列进行查询: 假设有一个复合索引 (A, B)。...【详细内容】
2024-01-15  小王爱编程  今日头条  Tags:mysql索引   点击:(87)  评论:(0)  加入收藏
站内最新
站内热门
站内头条