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

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

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

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

一、背景與價(jià)值

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

  • 原生配置方案:通過日志框架直接輸出基礎(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ú)顯示,無法直接拼接完整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語句的id
            String sqlId = ms.getId();
            log.info("sqlId = " + sqlId);
            // 獲取節(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();
        // sql語句中多個(gè)空格都用一個(gè)空格代替
        String sql = boundSql.getSql().replaceAll("[\\s]+", " ");
        if (!CollectionUtils.isEmpty(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;
    }
}

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é)

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

  • 快速定位SQL執(zhí)行問題
  • 直觀分析實(shí)際執(zhí)行的SQL語句
  • 有效識(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)文章

  • 你知道怎么從Python角度學(xué)習(xí)Java基礎(chǔ)

    你知道怎么從Python角度學(xué)習(xí)Java基礎(chǔ)

    這篇文章主要為大家詳細(xì)介紹了Python角度學(xué)習(xí)Java基礎(chǔ)的方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-02-02
  • Java多線程之循環(huán)柵欄技術(shù)CyclicBarrier使用探索

    Java多線程之循環(huán)柵欄技術(shù)CyclicBarrier使用探索

    這篇文章主要介紹了Java多線程之循環(huán)柵欄技術(shù)CyclicBarrier,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪<BR>
    2024-01-01
  • SpringBoot預(yù)加載與懶加載實(shí)現(xiàn)方法超詳細(xì)講解

    SpringBoot預(yù)加載與懶加載實(shí)現(xiàn)方法超詳細(xì)講解

    Spring一直被詬病啟動(dòng)時(shí)間慢,可Spring/SpringBoot是輕量級(jí)的框架。因?yàn)楫?dāng)Spring項(xiàng)目越來越大的時(shí)候,在啟動(dòng)時(shí)加載和初始化Bean就會(huì)變得越來越慢,很多時(shí)候我們?cè)趩?dòng)時(shí)并不需要加載全部的Bean,在調(diào)用時(shí)再加載就行,那這就需要預(yù)加載與懶加載的功能了
    2022-11-11
  • 解決@ConfigurationProperties注解的使用及亂碼問題

    解決@ConfigurationProperties注解的使用及亂碼問題

    這篇文章主要介紹了解決@ConfigurationProperties注解的使用及亂碼問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-10-10
  • JavaWeb評(píng)論功能實(shí)現(xiàn)步驟以及代碼實(shí)例

    JavaWeb評(píng)論功能實(shí)現(xiàn)步驟以及代碼實(shí)例

    項(xiàng)目初始版本上線,有時(shí)間寫點(diǎn)東西記錄一下項(xiàng)目中的心得體會(huì),通過這個(gè)項(xiàng)目學(xué)習(xí)了很多,要寫下來的有很多,先從評(píng)論功能開始吧,下面這篇文章主要給大家介紹了關(guān)于JavaWeb評(píng)論功能實(shí)現(xiàn)步驟以及代碼的相關(guān)資料,需要的朋友可以參考下
    2023-01-01
  • 詳解Java中對(duì)象池的介紹與使用

    詳解Java中對(duì)象池的介紹與使用

    對(duì)象池,顧名思義就是一定數(shù)量的已經(jīng)創(chuàng)建好的對(duì)象(Object)的集合。這篇文章主要為大家介紹了Java中對(duì)象池的介紹與使用,感興趣的可以了解一下
    2023-02-02
  • Java分布式服務(wù)框架Dubbo介紹

    Java分布式服務(wù)框架Dubbo介紹

    這篇文章介紹了Java分布式服務(wù)框架Dubbo,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06
  • java 基礎(chǔ)之JavaBean屬性命名規(guī)范問題

    java 基礎(chǔ)之JavaBean屬性命名規(guī)范問題

    這篇文章主要介紹了java 基礎(chǔ)之JavaBean屬性命名規(guī)范問題的相關(guān)資料,需要的朋友可以參考下
    2017-05-05
  • Javaweb開發(fā)環(huán)境Myeclipse6.5 JDK1.6 Tomcat6.0 SVN1.8配置教程

    Javaweb開發(fā)環(huán)境Myeclipse6.5 JDK1.6 Tomcat6.0 SVN1.8配置教程

    這篇文章主要介紹了Javaweb開發(fā)環(huán)境Myeclipse6.5 JDK1.6 Tomcat6.0 SVN1.8配置教程,感興趣的小伙伴們可以參考一下
    2016-06-06
  • java?poi之XWPFDocument如何讀取word內(nèi)容并創(chuàng)建新的word

    java?poi之XWPFDocument如何讀取word內(nèi)容并創(chuàng)建新的word

    這篇文章主要介紹了java?poi之XWPFDocument如何讀取word內(nèi)容并創(chuàng)建新的word問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2025-04-04

最新評(píng)論