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

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

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

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

一、背景與價值

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

  • 原生配置方案:通過日志框架直接輸出基礎(chǔ)SQL日志
  • 增強方案:使用MyBatis攔截器實現(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ù)值單獨顯示,無法直接拼接完整SQL
  • 缺乏執(zhí)行耗時統(tǒng)計
  • 動態(tài)SQL處理不夠直觀

三、增強方案:自定義攔截器實現(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é)點的id,即sql語句的id
            String sqlId = ms.getId();
            log.info("sqlId = " + sqlId);
            // 獲取節(jié)點的配置
            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é)點id + sql語句
    public static String getSql(Configuration configuration, BoundSql boundSql, String sqlId) {
        return sqlId + ":" + showSql(configuration, boundSql);
    }
    // 進行?的替換
    public static String showSql(Configuration configuration, BoundSql boundSql) {
        // 獲取參數(shù)
        Object parameterObject = boundSql.getParameterObject();
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        // sql語句中多個空格都用一個空格代替
        String sql = boundSql.getSql().replaceAll("[\\s]+", " ");
        if (!CollectionUtils.isEmpty(parameterMappings) && parameterObject != null) {
            // 獲取類型處理器注冊器,類型處理器的功能是進行java類型和數(shù)據(jù)庫類型的轉(zhuǎn)換
            TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
            // 如果根據(jù)parameterObject.getClass()可以找到對應(yīng)的類型,則替換
            if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                sql = sql.replaceFirst("\\?",
                        Matcher.quoteReplacement(getParameterValue(parameterObject)));
            } else {
                // MetaObject主要是封裝了originalObject對象,提供了get和set的方法用于獲取和設(shè)置originalObject的屬性值,主要支持對JavaBean、Collection、Map三種類型對象的操作
                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)) {
                        // 該分支是動態(tài)sql
                        Object obj = boundSql.getAdditionalParameter(propertyName);
                        sql = sql.replaceFirst("\\?",
                                Matcher.quoteReplacement(getParameterValue(obj)));
                    } else {
                        // 打印出缺失,提醒該參數(shù)缺失并防止錯位
                        sql = sql.replaceFirst("\\?", "缺失");
                    }
                }
            }
        }
        return sql;
    }
    // 如果參數(shù)是String,則添加單引號, 如果是日期,則轉(zhuǎn)換為時間格式器并加單引號; 對參數(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í)行耗時監(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) {
    }
}

自定義攔截器之后,請注意配置該攔截器

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í)行耗時: 48ms
WARN  [http-nio-8080-exec-1] - 慢SQL警告: 執(zhí)行耗時1204ms

四.總結(jié)

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

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

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

相關(guān)文章

  • StringRedisTemplate操作hash實現(xiàn)過程

    StringRedisTemplate操作hash實現(xiàn)過程

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

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

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

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

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

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

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

    Mybatis中返回主鍵值方式

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

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

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

    uploadify上傳及后臺文件合法性驗證的代碼解析

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

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

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

    SpringBoot中的application.properties無法加載問題定位技巧

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

    Docker部署Java項目實踐圖文教程

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

最新評論

东阳市| 仁化县| 交口县| 霞浦县| 环江| 枣庄市| 廉江市| 南部县| 和田县| 湖南省| 木里| 安顺市| 庆元县| 游戏| 广南县| 青田县| 临夏市| 鸡西市| 日土县| 连云港市| 茶陵县| 九龙城区| 蓬莱市| 华容县| 图木舒克市| 淮滨县| 商洛市| 平泉县| 商河县| 利川市| 桂林市| 元阳县| 石屏县| 西安市| 呈贡县| 阿城市| 贵溪市| 泌阳县| 即墨市| 晋宁县| 桑日县|