0


mybatis sql 打印

mybatis sql 打印

场景

在开发环境,需要实时查看 mybatis 打印的完整sql ,也可以使用插件手动复制转换,比较麻烦,现在尝试通过 java agent方式进行拦截打印完整 sql , 字节码技术 bytebuddy, 当然也可以使用javaassist

实现

目前支持 mysql 驱动 8.x

引入依赖

  1. <properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><bytebuddy.version>1.14.18</bytebuddy.version><logback.version>1.5.6</logback.version></properties><dependencies><!--sql 格式化--><dependency><groupId>com.github.vertical-blank</groupId><artifactId>sql-formatter</artifactId><version>2.0.5</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.34</version><optional>true</optional></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><dependency><groupId>net.bytebuddy</groupId><artifactId>byte-buddy</artifactId><version>${bytebuddy.version}</version></dependency><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><version>${mysql.version}</version><optional>true</optional></dependency><dependency><groupId>net.bytebuddy</groupId><artifactId>byte-buddy-agent</artifactId><version>${bytebuddy.version}</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>2.0.13</version><optional>true</optional></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-core</artifactId><version>${logback.version}</version><optional>true</optional></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>${logback.version}</version><optional>true</optional></dependency></dependencies><build><finalName>mybatis-log</finalName><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-shade-plugin</artifactId><version>3.2.4</version><executions><execution><phase>package</phase><goals><goal>shade</goal></goals><configuration><createDependencyReducedPom>false</createDependencyReducedPom><transformers><transformerimplementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"><manifestEntries><!-- 修改为正确的Premain地址 --><Premain-Class>org.example.sample.log.SqlLogAgent</Premain-Class></manifestEntries></transformer></transformers><filters><filter><artifact>*:*</artifact><excludes><exclude>META-INF/*.SF</exclude><exclude>META-INF/*.DSA</exclude><exclude>META-INF/*.RSA</exclude></excludes></filter></filters></configuration></execution></executions></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><configuration><source>1.8</source><target>1.8</target></configuration></plugin></plugins></build>

SqlLogAgent 定义入口

  1. publicclassSqlLogAgent{publicstaticvoidpremain(String agentArgs,Instrumentation inst){//目前 mysql 8.x//拦截 ClientPreparedQuerynewAgentBuilder.Default().type(ElementMatchers.named("com.mysql.cj.ClientPreparedQuery")).transform((builder, typeDescription, classLoader,module,protectionDomain)->
  2. builder.method(ElementMatchers.named("fillSendPacket"))//增加逻辑委托给 MysqlCJLoggerInterceptor.intercept(MethodDelegation.to(MysqlCJLoggerInterceptor.class))).installOn(inst);}}

MysqlCJLoggerInterceptor 增强逻辑

  1. @Slf4jpublicclassMysqlCJLoggerInterceptor{/*
  2. @Advice.OnMethodEnter(suppress = Throwable.class)
  3. public static void onSetParameterEnter(@Advice.Origin("#t") String methodName) throws SQLException {
  4. log.info("agent enter {}", methodName);
  5. System.out.println("agent enter: "+methodName);
  6. }
  7. @Advice.OnMethodExit(suppress = Throwable.class)
  8. public static void onFillSendPacketExit(@Advice.Origin("#t") String methodName,
  9. @Advice.Return Message message) throws SQLException {
  10. try {
  11. log.info("methodName: {}", methodName);
  12. String s = new String(message.getByteBuffer());
  13. log.info("mysql log: {}", s);
  14. } catch (Exception e) {
  15. log.error("onFillSendPacketEnter error:", e);
  16. }
  17. }*//* @Advice.OnMethodEnter(suppress = Throwable.class)
  18. public static void onEnter(@Advice.Origin MethodDescription method,
  19. @Advice.This Object instance) {
  20. System.out.println("Method " + method.getName() + " entered in " + instance.getClass().getSimpleName());
  21. }
  22. @Advice.OnMethodExit(suppress = Throwable.class)
  23. public static void onExit(@Advice.Origin MethodDescription method,
  24. @Advice.Return(readOnly = false) Object returnValue) {
  25. System.out.println("Method " + method.getName() + " exited in " + method.getDeclaringType().asErasure().getInternalName());
  26. }*///========================以上都没有效果=======================////拦截运行时@RuntimeTypepublicstaticObjectintercept(@OriginMethod method,@SuperCallCallable<?> callable)throwsException{StopWatch stopWatch =newStopWatch();
  27. stopWatch.start();Message call =(Message) callable.call();
  28. stopWatch.stop();String sql =newString(call.getByteBuffer());formatSqlLog(sql.trim(),stopWatch);return call;}/**
  29. * 格式化 sql 日志
  30. * @param sql
  31. * @param stopWatch
  32. */privatestaticvoidformatSqlLog(String sql,StopWatch stopWatch){String format ="\n--------------------【sql log】---------------------\n\n {} \n\n耗时: {}";
  33. log.info(format,sql,prettyPrint(stopWatch));}/**
  34. * 格式化
  35. *
  36. * @param stopWatch
  37. * @return
  38. */publicstaticStringprettyPrint(StopWatch stopWatch){StringBuilder sb =newStringBuilder(stopWatch.getTotalTimeMillis()+"ms");
  39. sb.append(" ");
  40. sb.append(stopWatch.getTotalTimeNanos()+"ns");
  41. sb.append('\n');if(stopWatch.getTaskInfo()==null){
  42. sb.append("No task info kept");}else{
  43. sb.append("---------------------------------------------\n");
  44. sb.append("ns % Task name\n");
  45. sb.append("---------------------------------------------\n");NumberFormat nf =NumberFormat.getNumberInstance();
  46. nf.setMinimumIntegerDigits(9);
  47. nf.setGroupingUsed(false);NumberFormat pf =NumberFormat.getPercentInstance();
  48. pf.setMinimumIntegerDigits(3);
  49. pf.setGroupingUsed(false);for(StopWatch.TaskInfo task : stopWatch.getTaskInfo()){
  50. sb.append(nf.format(task.getTimeNanos())).append(" ");
  51. sb.append(pf.format((double) task.getTimeNanos()/ stopWatch.getTotalTimeNanos())).append(" ");
  52. sb.append(task.getTaskName()).append("\n");}}return sb.toString();}}

测试

在项目启动时加入

  1. -javaagent:D:\xx\mybatis-log.jar

, 结果:

  1. --------------------【sql log】---------------------
  2. select address from t_address
  3. WHERE address like concat('%','shanghai','%')
  4. and is_delete =0
  5. 耗时: 0ms 393200ns
  6. ---------------------------------------------
  7. ns % Task name
  8. ---------------------------------------------
  9. 000393200 100%

good luck !

标签: mybatis

本文转载自: https://blog.csdn.net/u013887008/article/details/141356360
版权归原作者 重生之我是一名程序员 所有, 如有侵权,请联系我们删除。

“mybatis sql 打印”的评论:

还没有评论