0


mybatis sql 打印

mybatis sql 打印

场景

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

实现

目前支持 mysql 驱动 8.x

引入依赖

<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 定义入口

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

MysqlCJLoggerInterceptor 增强逻辑

@Slf4jpublicclassMysqlCJLoggerInterceptor{/*
    @Advice.OnMethodEnter(suppress = Throwable.class)
    public static void onSetParameterEnter(@Advice.Origin("#t") String methodName) throws SQLException {
        log.info("agent enter {}", methodName);
        System.out.println("agent enter: "+methodName);
    }
    @Advice.OnMethodExit(suppress = Throwable.class)
    public static void onFillSendPacketExit(@Advice.Origin("#t") String methodName,
                                             @Advice.Return Message message) throws SQLException {
        try {
            log.info("methodName: {}", methodName);
            String s = new String(message.getByteBuffer());
            log.info("mysql log: {}", s);
        } catch (Exception e) {
            log.error("onFillSendPacketEnter error:", e);
        }
    }*//* @Advice.OnMethodEnter(suppress = Throwable.class)
    public static void onEnter(@Advice.Origin MethodDescription method,
                               @Advice.This Object instance) {
        System.out.println("Method " + method.getName() + " entered in " + instance.getClass().getSimpleName());
    }

    @Advice.OnMethodExit(suppress = Throwable.class)
    public static void onExit(@Advice.Origin MethodDescription method,
                              @Advice.Return(readOnly = false) Object returnValue) {
        System.out.println("Method " + method.getName() + " exited in " + method.getDeclaringType().asErasure().getInternalName());
    }*///========================以上都没有效果=======================////拦截运行时@RuntimeTypepublicstaticObjectintercept(@OriginMethod method,@SuperCallCallable<?> callable)throwsException{StopWatch stopWatch =newStopWatch();
        stopWatch.start();Message call =(Message) callable.call();
        stopWatch.stop();String sql =newString(call.getByteBuffer());formatSqlLog(sql.trim(),stopWatch);return call;}/**
     * 格式化 sql 日志
     * @param sql
     * @param stopWatch
     */privatestaticvoidformatSqlLog(String sql,StopWatch stopWatch){String format ="\n--------------------【sql log】---------------------\n\n  {} \n\n耗时: {}";
        log.info(format,sql,prettyPrint(stopWatch));}/**
     * 格式化
     *
     * @param stopWatch
     * @return
     */publicstaticStringprettyPrint(StopWatch stopWatch){StringBuilder sb =newStringBuilder(stopWatch.getTotalTimeMillis()+"ms");
        sb.append("   ");
        sb.append(stopWatch.getTotalTimeNanos()+"ns");
        sb.append('\n');if(stopWatch.getTaskInfo()==null){
            sb.append("No task info kept");}else{
            sb.append("---------------------------------------------\n");
            sb.append("ns         %     Task name\n");
            sb.append("---------------------------------------------\n");NumberFormat nf =NumberFormat.getNumberInstance();
            nf.setMinimumIntegerDigits(9);
            nf.setGroupingUsed(false);NumberFormat pf =NumberFormat.getPercentInstance();
            pf.setMinimumIntegerDigits(3);
            pf.setGroupingUsed(false);for(StopWatch.TaskInfo task : stopWatch.getTaskInfo()){
                sb.append(nf.format(task.getTimeNanos())).append("  ");
                sb.append(pf.format((double) task.getTimeNanos()/ stopWatch.getTotalTimeNanos())).append("  ");
                sb.append(task.getTaskName()).append("\n");}}return sb.toString();}}

测试

在项目启动时加入

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

, 结果:

--------------------【sql log】---------------------

  select address from t_address
         WHERE  address like concat('%','shanghai','%')
            and is_delete =0 

耗时: 0ms   393200ns
---------------------------------------------
ns         %     Task name
---------------------------------------------
000393200  100%  

good luck !

标签: mybatis

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

“mybatis sql 打印”的评论:

还没有评论