最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

如何使用MyBatis/MyBatis?Plus實(shí)現(xiàn)SQL日志打印與執(zhí)行監(jiān)控

 更新時(shí)間:2025年05月16日 09:42:18   作者:sjsjsbbsbsn  
MyBatis默認(rèn)的日志輸出僅顯示帶占位符的SQL語(yǔ)句,無(wú)法直接看到實(shí)際參數(shù)值,且缺乏執(zhí)行時(shí)間統(tǒng)計(jì),本文將介紹兩種實(shí)現(xiàn)方案,感興趣的朋友跟隨小編一起看看吧

使用MyBatis/MyBatis Plus實(shí)現(xiàn)SQL日志打印與執(zhí)行監(jiān)控

一、背景與價(jià)值

在開(kāi)發(fā)過(guò)程中,SQL日志的完整輸出對(duì)于調(diào)試和性能優(yōu)化至關(guān)重要。MyBatis默認(rèn)的日志輸出僅顯示帶占位符的SQL語(yǔ)句,無(wú)法直接看到實(shí)際參數(shù)值,且缺乏執(zhí)行時(shí)間統(tǒng)計(jì)。本文將介紹兩種實(shí)現(xiàn)方案:

  • 原生配置方案:通過(guò)日志框架直接輸出基礎(chǔ)SQL日志
  • 增強(qiáng)方案:使用MyBatis攔截器實(shí)現(xiàn)完整SQL打印和執(zhí)行監(jiān)控

二、原生配置方案(快速上手)

1. 日志框架配置(以Logback為例)

<!-- logback-spring.xml -->
<configuration>
    <logger name="com.zaxxer.hikari" level="INFO"/>
    <logger name="java.sql.Connection" level="INFO"/>
    <logger name="java.sql.Statement" level="DEBUG"/>
    <logger name="java.sql.PreparedStatement" level="DEBUG"/>
</configuration>

2. 輸出示例

DEBUG [main] - ==>  Preparing: SELECT * FROM user WHERE name = ?
DEBUG [main] - ==> Parameters: John(String)

3. 局限性

  • 參數(shù)值單獨(dú)顯示,無(wú)法直接拼接完整SQL
  • 缺乏執(zhí)行耗時(shí)統(tǒng)計(jì)
  • 動(dòng)態(tài)SQL處理不夠直觀

三、增強(qiáng)方案:自定義攔截器實(shí)現(xiàn)

1. SQL美化與參數(shù)替換

public class MybatisPlusAllSqlLog implements InnerInterceptor {
    public static final Logger log = LoggerFactory.getLogger("sys-sql");
    @Override
    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        logInfo(boundSql, ms, parameter);
    }
    @Override
    public void beforeUpdate(Executor executor, MappedStatement ms, Object parameter) throws SQLException {
        BoundSql boundSql = ms.getBoundSql(parameter);
        logInfo(boundSql, ms, parameter);
    }
    private static void logInfo(BoundSql boundSql, MappedStatement ms, Object parameter) {
        try {
            log.info("parameter = " + parameter);
            // 獲取到節(jié)點(diǎn)的id,即sql語(yǔ)句的id
            String sqlId = ms.getId();
            log.info("sqlId = " + sqlId);
            // 獲取節(jié)點(diǎn)的配置
            Configuration configuration = ms.getConfiguration();
            // 獲取到最終的sql語(yǔ)句
            String sql = getSql(configuration, boundSql, sqlId);
            log.info("完整的sql:{}", sql);
        } catch (Exception e) {
            log.error("異常:{}", e.getLocalizedMessage(), e);
        }
    }
    // 封裝了一下sql語(yǔ)句,使得結(jié)果返回完整xml路徑下的sql語(yǔ)句節(jié)點(diǎn)id + sql語(yǔ)句
    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();
        // sql語(yǔ)句中多個(gè)空格都用一個(gè)空格代替
        String sql = boundSql.getSql().replaceAll("[\\s]+", " ");
        if (!CollectionUtils.isEmpty(parameterMappings) && parameterObject != null) {
            // 獲取類型處理器注冊(cè)器,類型處理器的功能是進(jìn)行java類型和數(shù)據(jù)庫(kù)類型的轉(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;
    }
}

2. 執(zhí)行耗時(shí)監(jiān)控

@Intercepts({
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class,
                Object.class}),
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class,
                Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class})})
public class SqlStatementInterceptor implements Interceptor {
    public static final Logger log = LoggerFactory.getLogger("sys-sql");
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        long startTime = System.currentTimeMillis();
        try {
            return invocation.proceed();
        } finally {
            long timeConsuming = System.currentTimeMillis() - startTime;
            log.info("執(zhí)行SQL:{}ms", timeConsuming);
            if (timeConsuming > 999 && timeConsuming < 5000) {
                log.info("執(zhí)行SQL大于1s:{}ms", timeConsuming);
            } else if (timeConsuming >= 5000 && timeConsuming < 10000) {
                log.info("執(zhí)行SQL大于5s:{}ms", timeConsuming);
            } else if (timeConsuming >= 10000) {
                log.info("執(zhí)行SQL大于10s:{}ms", timeConsuming);
            }
        }
    }
    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }
    @Override
    public void setProperties(Properties properties) {
    }
}

自定義攔截器之后,請(qǐng)注意配置該攔截器

3.輸出示例

INFO  [http-nio-8080-exec-1] - SQLID: com.example.mapper.UserMapper.selectById
INFO  [http-nio-8080-exec-1] - 完整SQL: SELECT id,name,age FROM user WHERE id=1
INFO  [http-nio-8080-exec-1] - 執(zhí)行耗時(shí): 48ms
WARN  [http-nio-8080-exec-1] - 慢SQL警告: 執(zhí)行耗時(shí)1204ms

四.總結(jié)

通過(guò)合理配置SQL日志輸出,開(kāi)發(fā)者可以:

  • 快速定位SQL執(zhí)行問(wèn)題
  • 直觀分析實(shí)際執(zhí)行的SQL語(yǔ)句
  • 有效識(shí)別性能瓶頸
  • 提升動(dòng)態(tài)SQL調(diào)試效率

到此這篇關(guān)于使用MyBatisMyBatis Plus實(shí)現(xiàn)SQL日志打印與執(zhí)行監(jiān)控的文章就介紹到這了,更多相關(guān)MyBatisMyBatis Plus SQL日志打印內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • StringRedisTemplate操作hash實(shí)現(xiàn)過(guò)程

    StringRedisTemplate操作hash實(shí)現(xiàn)過(guò)程

    文章總結(jié)了使用StringRedisTemplate操作Redis哈希(hash)的基本示例,分享了個(gè)人經(jīng)驗(yàn),旨在為讀者提供參考,并鼓勵(lì)大家支持腳本之家
    2026-03-03
  • Java中常用的設(shè)計(jì)模式之責(zé)任鏈模式詳解

    Java中常用的設(shè)計(jì)模式之責(zé)任鏈模式詳解

    這篇文章主要為大家詳細(xì)介紹了Java中常用的設(shè)計(jì)模式之責(zé)任鏈模式,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助
    2022-02-02
  • JAVA實(shí)現(xiàn)數(shù)字大寫金額轉(zhuǎn)換的方法

    JAVA實(shí)現(xiàn)數(shù)字大寫金額轉(zhuǎn)換的方法

    這篇文章主要介紹了JAVA實(shí)現(xiàn)數(shù)字大寫金額轉(zhuǎn)換的方法,涉及java針對(duì)字符串與數(shù)組的遍歷與轉(zhuǎn)換相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-07-07
  • Java使用Spire.PDF for Java實(shí)現(xiàn)旋轉(zhuǎn)PDF頁(yè)面

    Java使用Spire.PDF for Java實(shí)現(xiàn)旋轉(zhuǎn)PDF頁(yè)面

    在日常的文檔處理工作中,我們經(jīng)常會(huì)遇到 PDF 頁(yè)面方向不正確的問(wèn)題,本文將深入探討如何利用 Java 實(shí)現(xiàn) PDF 頁(yè)面的旋轉(zhuǎn)操作,感興趣的小伙伴可以了解下
    2025-09-09
  • Mybatis中返回主鍵值方式

    Mybatis中返回主鍵值方式

    這篇文章主要介紹了Mybatis中返回主鍵值方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • java中的內(nèi)部類內(nèi)部接口用法說(shuō)明

    java中的內(nèi)部類內(nèi)部接口用法說(shuō)明

    這篇文章主要介紹了java中的內(nèi)部類內(nèi)部接口用法說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-09-09
  • uploadify上傳及后臺(tái)文件合法性驗(yàn)證的代碼解析

    uploadify上傳及后臺(tái)文件合法性驗(yàn)證的代碼解析

    這篇文章主要介紹了uploadify上傳及后臺(tái)文件合法性驗(yàn)證的代碼解析,整段代碼分為后臺(tái)上傳方法,文件合法性驗(yàn)證類,前端上傳js,非常不錯(cuò)具有參考借鑒價(jià)值,需要的朋友可以參考下
    2016-11-11
  • visual studio 2019安裝配置可編寫c/c++語(yǔ)言的IDE環(huán)境

    visual studio 2019安裝配置可編寫c/c++語(yǔ)言的IDE環(huán)境

    這篇文章主要介紹了visual studio 2019安裝配置可編寫c/c++語(yǔ)言的IDE環(huán)境,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-03-03
  • SpringBoot中的application.properties無(wú)法加載問(wèn)題定位技巧

    SpringBoot中的application.properties無(wú)法加載問(wèn)題定位技巧

    這篇文章主要介紹了SpringBoot中的application.properties無(wú)法加載問(wèn)題定位技巧,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • Docker部署Java項(xiàng)目實(shí)踐圖文教程

    Docker部署Java項(xiàng)目實(shí)踐圖文教程

    通過(guò)使用Docker,可以實(shí)現(xiàn)Java應(yīng)用程序的快速部署、環(huán)境隔離和擴(kuò)展,下面這篇文章主要介紹了Docker部署Java項(xiàng)目的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2025-11-11

最新評(píng)論

开阳县| 柏乡县| 繁峙县| 北海市| 海门市| 诏安县| 江永县| 柘城县| 醴陵市| 浠水县| 遂川县| 永嘉县| 顺平县| 洛隆县| 柘城县| 万源市| 石城县| 宁波市| 体育| 大厂| 滨海县| 高雄市| 馆陶县| 留坝县| 远安县| 万全县| 桑植县| 乌审旗| 礼泉县| 肇东市| 得荣县| 德清县| 定安县| 宽甸| 乐陵市| 福建省| 贵阳市| 南开区| 津市市| 独山县| 偏关县|