欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

使用MybatisPlus實(shí)現(xiàn)sql日志打印優(yōu)化

 更新時(shí)間:2025年08月01日 09:36:18   作者:CAT_cwds  
本文主要介紹了使用MybatisPlus實(shí)現(xiàn)sql日志打印優(yōu)化,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

背景:

在排查無憂行后臺(tái)服務(wù)日志時(shí),一個(gè)請(qǐng)求可能會(huì)包含多個(gè)執(zhí)行的sql,經(jīng)常會(huì)遇到SQL語句與對(duì)應(yīng)參數(shù)不連續(xù)顯示,或者參數(shù)較多需要逐個(gè)匹配的情況。這種情況下,如果需要還原完整SQL語句就會(huì)比較耗時(shí)。因此,我希望能優(yōu)化這一流程。

正文:

在平時(shí)排查sql執(zhí)行情況時(shí),對(duì)于sql查詢參數(shù)比較多或者多個(gè)sql在同一個(gè)請(qǐng)求下執(zhí)行的時(shí)候會(huì)比較麻煩,需要查看每個(gè)參數(shù)的值一一對(duì)應(yīng)檢查是否有異常,有時(shí)候還需要將查詢sql的問號(hào)還原去庫里面查詢結(jié)果,就類似下面的日志:

一個(gè)一個(gè)參數(shù)還原比較麻煩,因此針對(duì)這種問題,我找到了一個(gè)解決辦法,重寫MybatisPlus的InnerInterceptor,將sql日志重新組裝,那么在查看sql日志時(shí)候就可以直接查看賦值好參數(shù)的sql了,將上面截圖的sql整合之后可以轉(zhuǎn)化為下面的sql:

具體的思路以及實(shí)現(xiàn)方案,以無憂行項(xiàng)目為例

實(shí)現(xiàn)思路,利用mybatisplus提供的接口InnerInterceptor,重寫sql日志,并將重寫之后的類注入到sqlSessionFactory中,在執(zhí)行sql之前捕獲到并打印。
解釋一下InnerInterceptor類:InnerInterceptor 是 MyBatis-Plus 框架中的一個(gè)核心攔截器接口,它是 MyBatis 攔截器機(jī)制的擴(kuò)展,用于在 SQL 執(zhí)行過程中進(jìn)行攔截和增強(qiáng)。
InnerInterceptor工作原理:InnerInterceptor 通過 MyBatis 的插件機(jī)制工作,在以下時(shí)機(jī)進(jìn)行攔截:

- beforeQuery:查詢操作執(zhí)行前
- beforeUpdate:更新操作執(zhí)行前
- beforePrepare:SQL 準(zhǔn)備階段

針對(duì)查詢sql執(zhí)行的sql重整,使用的是beforeQuery,針對(duì)更新sql執(zhí)行的重整,使用的是beforeUpdate,以下是具體實(shí)現(xiàn):

1.首先,添加依賴,

maven依賴:

<dependency>
   <groupId>com.baomidou</groupId>
   <artifactId>mybatis-plus-boot-starter</artifactId>
   <version>3.4.0</version>
</dependency>

或者gradle依賴:

compile("com.baomidou:mybatis-plus-generator:3.4.0")

2.重寫InnerInterceptor

package com.cmi.jego.micro.flight.admin.service.config;

import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
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.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 java.sql.SQLException;
import java.text.DateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.regex.Matcher;

/**
 * sql日志重寫
 *
 * @author zhouxy
 * @date 2025年05月08日 11:27
 */
@Slf4j
@org.springframework.context.annotation.Configuration
public class MybatisPlusLogRewrite implements InnerInterceptor {
    @Override
    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        log.info("beforeQuery");
        logInfo(boundSql, ms, parameter);
    }

    @Override
    public void beforeUpdate(Executor executor, MappedStatement ms, Object parameter) throws SQLException {
        log.info("beforeUpdate");
        BoundSql boundSql = ms.getBoundSql(parameter);
        logInfo(boundSql, ms, parameter);
    }

    private static void logInfo(BoundSql boundSql, MappedStatement ms, Object parameter) {
        try {
            // 獲取到節(jié)點(diǎn)的id,即sql語句的id
            String sqlId = ms.getId();
            // 獲取節(jié)點(diǎn)的配置
            Configuration configuration = ms.getConfiguration();
            // 獲取到最終的sql語句
            String sql = getSql(configuration, boundSql, sqlId);
            log.info("完整的sql:{}", sql);
        } catch (Exception e) {
            log.error("異常:{}", e.getLocalizedMessage(), e);
        }
    }

    // 封裝了一下sql語句,使得結(jié)果返回完整xml路徑下的sql語句節(jié)點(diǎn)id + sql語句
    public static String getSql(Configuration configuration, BoundSql boundSql, String sqlId) {
        return sqlId + ":" + showSql(configuration, boundSql);
    }

    // 進(jìn)行?的替換
    public static String showSql(Configuration configuration, BoundSql boundSql) {
        // 獲取參數(shù)
        Object parameterObject = boundSql.getParameterObject();
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
//        log.info("boundSql:{}",boundSql.getSql());
        // sql語句中多個(gè)空格都用一個(gè)空格代替
        String sql = boundSql.getSql().replaceAll("[\\s]+", " ");
        if (CollectionUtils.isNotEmpty(parameterMappings) && parameterObject != null) {
            // 獲取類型處理器注冊(cè)器,類型處理器的功能是進(jìn)行java類型和數(shù)據(jù)庫類型的轉(zhuǎn)換
            TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
            // 如果根據(jù)parameterObject.getClass()可以找到對(duì)應(yīng)的類型,則替換
            if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                sql = sql.replaceFirst("\\?",
                        Matcher.quoteReplacement(getParameterValue(parameterObject)));
            } else {
                // MetaObject主要是封裝了originalObject對(duì)象,提供了get和set的方法用于獲取和設(shè)置originalObject的屬性值,主要支持對(duì)JavaBean、Collection、Map三種類型對(duì)象的操作
                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)) {
                        // 該分支是動(dòng)態(tài)sql
                        Object obj = boundSql.getAdditionalParameter(propertyName);
                        sql = sql.replaceFirst("\\?",
                                Matcher.quoteReplacement(getParameterValue(obj)));
                    } else {
                        // 打印出缺失,提醒該參數(shù)缺失并防止錯(cuò)位
                        sql = sql.replaceFirst("\\?", "缺失");
                    }
                }
            }
        }
        return sql;
    }

    // 如果參數(shù)是String,則添加單引號(hào), 如果是日期,則轉(zhuǎn)換為時(shí)間格式器并加單引號(hào); 對(duì)參數(shù)是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;
    }
}

3.將當(dāng)前的重寫類MybatisPlusLogRewrite注入到sqlSessionFactory中。

package com.cmi.jego.micro.flight.admin.service.config;

import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;
import java.util.List;

/**
 * @author zhouxy
 * @date 2025年05月09日 15:58
 */
@Configuration
public class MyBatisPlusConfig {

    @Autowired
    private List<SqlSessionFactory> sqlSessionFactoryList;

    @Autowired
    private MybatisPlusLogRewrite myInnerInterceptor;

    /**
     * 添加Mybatis攔截器
     *
     * @author zhouxy
     * @date 2025/5/15 16:29
     */
    @PostConstruct
    public void addMybatisInterceptor() {
        for (SqlSessionFactory sqlSessionFactory : sqlSessionFactoryList) {
            org.apache.ibatis.session.Configuration configuration = sqlSessionFactory.getConfiguration();
            //將sql攔截器添加到MybatisPlusInterceptor攔截器鏈
            MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
            mybatisPlusInterceptor.addInnerInterceptor(myInnerInterceptor);
            configuration.addInterceptor(mybatisPlusInterceptor);
        }
    }
}

關(guān)閉其他sql打印日志,執(zhí)行查詢sql,只打印當(dāng)前重寫之后的sql日志如下:

2025-05-14 18:03:01.353  INFO 34718 --- [           main] c.c.j.m.f.a.service.handler.LogHandler   : function:[OrderRefundServiceImpl.queryOrderListStatusCount], request: {"lang":"zh_CN","pageNum":1,"pageSize":10}
2025-05-14 18:03:01.385  INFO 34718 --- [           main] c.c.j.m.f.a.s.c.MybatisPlusLogRewrite    : beforeQuery
2025-05-14 18:03:01.387  INFO 34718 --- [           main] c.c.j.m.f.a.s.c.MybatisPlusLogRewrite    : 完整的sql:com.cmi.jego.micro.flight.admin.service.mapper.jegotrip.TicketRefundMapper.queryListStatusCount:select distinct(tr.id) as refundId, tr.status from ticket_refund tr left join ticket_order td on tr.order_id = td.order_id left join ticket_passenger tp on tr.order_id = tp.order_id left join ticket_order_detail tod on tr.order_id = tod.order_id WHERE ( td.search_channel in ( 'hbgj-api-domestic-custom' ) or (td.order_source is null and td.search_channel is null) or td.order_source = '0' )
2025-05-14 18:03:01.395  WARN 34718 --- [           main] c.a.druid.pool.DruidAbstractDataSource   : discard long time none received connection. , jdbcUrl : jdbc:mysql://localhost:3306/ticket_jegotrip?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2B8, version : 1.2.5, lastPacketReceivedIdleMillis : 263261
2025-05-14 18:03:01.395  WARN 34718 --- [           main] c.a.druid.pool.DruidAbstractDataSource   : discard long time none received connection. , jdbcUrl : jdbc:mysql://localhost:3306/ticket_jegotrip?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2B8, version : 1.2.5, lastPacketReceivedIdleMillis : 263912
2025-05-14 18:03:01.396  WARN 34718 --- [           main] c.a.druid.pool.DruidAbstractDataSource   : discard long time none received connection. , jdbcUrl : jdbc:mysql://localhost:3306/ticket_jegotrip?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2B8, version : 1.2.5, lastPacketReceivedIdleMillis : 263933
2025-05-14 18:03:01.396  WARN 34718 --- [           main] c.a.druid.pool.DruidAbstractDataSource   : discard long time none received connection. , jdbcUrl : jdbc:mysql://localhost:3306/ticket_jegotrip?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2B8, version : 1.2.5, lastPacketReceivedIdleMillis : 263958
2025-05-14 18:03:01.396  WARN 34718 --- [           main] c.a.druid.pool.DruidAbstractDataSource   : discard long time none received connection. , jdbcUrl : jdbc:mysql://localhost:3306/ticket_jegotrip?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2B8, version : 1.2.5, lastPacketReceivedIdleMillis : 263986
2025-05-14 18:03:01.851  INFO 34718 --- [           main] c.c.j.m.f.a.s.c.MybatisPlusLogRewrite    : beforeQuery
2025-05-14 18:03:01.851  INFO 34718 --- [           main] c.c.j.m.f.a.s.c.MybatisPlusLogRewrite    : 完整的sql:com.cmi.jego.micro.flight.admin.service.mapper.jegotrip.TicketRefundMoneyMapper.queryFailedByRefundIds:SELECT id , order_id, user_id, refund_id, pay_order_id, supplier_order_id, type, price, refund_price, status, refund_time, limit_time, refund_no, create_time, update_time, remark FROM ticket_refund_money WHERE refund_id IN ( 1295 , 1296 , 1423 , 1424 ) AND status = 4
2025-05-14 18:03:01.871  INFO 34718 --- [           main] c.c.j.m.f.a.service.handler.LogHandler   : class:[OrderRefundServiceImpl.queryOrderListStatusCount] response: {"bizCode":0,"bizMsg":"SUCCESS","data":{"statusCounts":[{"count":1,"status":-1,"statusDesc":"已取消"},{"count":2,"status":3,"statusDesc":"已退票退款"},{"count":1,"status":4,"statusDesc":"退票異常"}]},"rpcCode":0,"rpcMsg":"SUCCESS"}, cost: 521,
2025-05-14 18:03:01.876  INFO 34718 --- [           main] c.c.j.m.f.a.s.s.OrderRefundServiceTest   : {"bizCode":0,"bizMsg":"SUCCESS","data":{"statusCounts":[{"count":1,"status":-1,"statusDesc":"已取消"},{"count":2,"status":3,"statusDesc":"已退票退款"},{"count":1,"status":4,"statusDesc":"退票異常"}]},"rpcCode":0,"rpcMsg":"SUCCESS"}

性能評(píng)估方面

重寫InnerInterceptor類其實(shí)是在SqlSessionFactory中添加了攔截器,那么性能如何呢?下面是我這邊實(shí)際測試的時(shí)長對(duì)比

不添加sql重寫:

添加sql重寫:

根據(jù)測試時(shí)間可以看出,加了sql重寫會(huì)比未加的時(shí)候的時(shí)間長了11ms。
因?yàn)樯婕暗綌r截以及反射,性能比不加的時(shí)候會(huì)稍微耗時(shí)一些,
如果是訪問量不高的情況下,用戶對(duì)于加沒加sql重寫的主觀感應(yīng)時(shí)長其實(shí)差不多,因此這種sql重寫方案適合后臺(tái)訪問并發(fā)量不高的情況使用。
如果是并發(fā)量較高且比較注重性能的情況下,不建議加。

到此這篇關(guān)于使用MybatisPlus實(shí)現(xiàn)sql日志打印優(yōu)化的文章就介紹到這了,更多相關(guān)MybatisPlus sql日志打印內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot自定義雪花算法生成ID的實(shí)現(xiàn)示例

    SpringBoot自定義雪花算法生成ID的實(shí)現(xiàn)示例

    雪花算法是一種生成唯一ID的分布式算法,它能生成不重復(fù)的、有時(shí)間順序的全局唯一ID,本文主要介紹了SpringBoot自定義雪花算法生成ID的實(shí)現(xiàn)示例,具有一定的參考價(jià)值,感興趣的可以了解一下
    2025-04-04
  • 公共POI導(dǎo)出Excel方法詳解

    公共POI導(dǎo)出Excel方法詳解

    這篇文章主要介紹了公共POI導(dǎo)出Excel方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-05-05
  • Spring中@RequestParam與@RequestBody的使用場景詳解

    Spring中@RequestParam與@RequestBody的使用場景詳解

    這篇文章主要介紹了Spring中@RequestParam與@RequestBody的使用場景詳解,注解@RequestParam接收的參數(shù)是來自requestHeader中即請(qǐng)求頭或body請(qǐng)求體,通常用于GET請(qǐng)求,比如常見的url等,需要的朋友可以參考下
    2023-12-12
  • jvm調(diào)優(yōu)的幾種場景(小結(jié))

    jvm調(diào)優(yōu)的幾種場景(小結(jié))

    本文主要介紹了jvm調(diào)優(yōu)的幾種場景,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-01-01
  • Spring Boot中使用Server-Sent Events (SSE) 實(shí)現(xiàn)實(shí)時(shí)數(shù)據(jù)推送教程

    Spring Boot中使用Server-Sent Events (SSE) 實(shí)

    Server-Sent Events (SSE) 是HTML5引入的一種輕量級(jí)的服務(wù)器向?yàn)g覽器客戶端單向推送實(shí)時(shí)數(shù)據(jù)的技術(shù),本文主要介紹了Spring Boot中使用Server-Sent Events (SSE) 實(shí)現(xiàn)實(shí)時(shí)數(shù)據(jù)推送教程,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-03-03
  • string boot 與 自定義interceptor的實(shí)例講解

    string boot 與 自定義interceptor的實(shí)例講解

    下面小編就為大家分享一篇string boot 與 自定義interceptor的實(shí)例講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2017-12-12
  • SpringBoot如何配置MySQL和Oracl雙數(shù)據(jù)源(Mybatis)

    SpringBoot如何配置MySQL和Oracl雙數(shù)據(jù)源(Mybatis)

    這篇文章主要介紹了SpringBoot如何配置MySQL和Oracl雙數(shù)據(jù)源(Mybatis)問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • SpringBoot結(jié)合Ajax實(shí)現(xiàn)登錄頁面實(shí)例

    SpringBoot結(jié)合Ajax實(shí)現(xiàn)登錄頁面實(shí)例

    大家好,本篇文章主要講的是SpringBoot結(jié)合Ajax實(shí)現(xiàn)登錄頁面實(shí)例,感興趣的同學(xué)趕快來看一看,對(duì)你有幫助的話記得收藏一下
    2022-02-02
  • Java字符串相關(guān)類操作方法詳解

    Java字符串相關(guān)類操作方法詳解

    這篇文章主要給大家介紹了關(guān)于Java字符串相關(guān)類操作的相關(guān)資料,文中還特別介紹了字符串的定義、構(gòu)造方法、內(nèi)存原理、比較方法和常見方法,同時(shí)還介紹了StringBuilder和StringJoiner類,需要的朋友可以參考下
    2024-12-12
  • Netty客戶端接入流程N(yùn)ioSocketChannel創(chuàng)建解析

    Netty客戶端接入流程N(yùn)ioSocketChannel創(chuàng)建解析

    這篇文章主要為大家介紹了Netty客戶端接入流程N(yùn)ioSocketChannel創(chuàng)建源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-03-03

最新評(píng)論