<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    Mybatis 調試輸出SQL語句,到底是如何實現的呢?

    VSole2022-05-29 17:28:29

    Java 開發中常用的幾款日志框架有很多種,并且這些日志框架來源于不同的開源組織,給用戶暴露的接口也有很多不同之處,所以很多開源框架會自己定義一套統一的日志接口,兼容上述第三方日志框架,供上層使用。

    一般實現的方式是使用適配器模式,將各個第三方日志框架接口轉換為框架內部自定義的日志接口。MyBatis 也提供了類似的實現,這里我們就來簡單了解一下。

    適配器模式是什么?

    簡單來說,適配器模式主要解決的是由于接口不能兼容而導致類無法使用的問題,這在處理遺留代碼以及集成第三方框架的時候用得比較多。其核心原理是:通過組合的方式,將需要適配的類轉換成使用者能夠使用的接口。

    日志模塊

    MyBatis 自定義的 Log 接口位于 org.apache.ibatis.logging 包中,相關的適配器也位于該包中。

    首先是 LogFactory 工廠類,它負責創建 Log 對象,在 LogFactory 類中有一段靜態代碼塊,其中會依次加載各個第三方日志框架的適配器。

    static {
        tryImplementation(LogFactory::useSlf4jLogging);
        tryImplementation(LogFactory::useCommonsLogging);
        tryImplementation(LogFactory::useLog4J2Logging);
        tryImplementation(LogFactory::useLog4JLogging);
        tryImplementation(LogFactory::useJdkLogging);
        tryImplementation(LogFactory::useNoLogging);
    }
    

    JDK Logging 的加載流程(useJdkLogging() 方法)為例,其具體代碼實現和注釋如下:

    /**
     * 首先會檢測 logConstructor 字段是否為空,
     * 1.如果不為空,則表示已經成功確定當前使用的日志框架,直接返回;
     * 2.如果為空,則在當前線程中執行傳入的 Runnable.run() 方法,嘗試確定當前使用的日志框架
     */
    private static void tryImplementation(Runnable runnable) {
        if (logConstructor == null) {
            try {
                runnable.run();
            } catch (Throwable t) {
                // ignore
            }
        }
    }
    
    public static synchronized void useJdkLogging() {
        setImplementation(org.apache.ibatis.logging.jdk14.Jdk14LoggingImpl.class);
    }
    
    private static void setImplementation(Class implClass) {
        try {
            // 獲取適配器的構造方法
            Constructor candidate = implClass.getConstructor(String.class);
            // 嘗試加載適配器,加載失敗會拋出異常
            Log log = candidate.newInstance(LogFactory.class.getName());
            // 加載成功,則更新logConstructor字段,記錄適配器的構造方法
            logConstructor = candidate;
        } catch (Throwable t) {
            throw new LogException("Error setting Log implementation.  Cause: " + t, t);
        }
    }
    

    打印SQL語句

    如何開啟打印

    這里演示Mybatis在運行時怎么輸出SQL語句,具體分析見原理章節。

    單獨使用Mybatis

    mybatis.xml配置文件中添加如下配置:

    <setting name="logImpl" value="STDOUT_LOGGING" />
    

    和SpringBoot整合

    有兩種方式,第一種也是利用StdOutImpl實現類去實現打印,在application.yml配置文件填寫如下:

    #mybatis配置
    mybatis:
      # 控制臺打印sql日志
      configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    

    其次我們還可以通過指定日志級別來輸出SQL語句:

    SpringBoot默認使用的SL4J(日志門面)+Logback(具體實現)的日志組合
    logging:
      level:
        xx包名: debug
    

    簡單分析原理

    這里我們直接看到org.apache.ibatis.executor.BaseExecutor#getConnection方法,了解Mybatis的應該都知道Mybatis在執行sql操作的時候會去獲取數據庫連接

    protected Connection getConnection(Log statementLog) throws SQLException {
        Connection connection = transaction.getConnection();
        // 判斷日志級別是否為Debug,是的話返回代理對象
        if (statementLog.isDebugEnabled()) {
            return ConnectionLogger.newInstance(connection, statementLog, queryStack);
        } else {
            return connection;
        }
    }
    

    可以看到我注釋的那行,它通過判斷日志級別來判斷是否返回ConnectionLogger代理對象,那么我們前面提到 Log 接口的實現類中StdOutImpl它的isDebugEnabled其實是永遠返回 true,代碼如下:

    并且它直接用的 System.println去輸出的SQL信息

    public class StdOutImpl implements Log {
        
      // ...省略無關代碼
          
      @Override
      public boolean isDebugEnabled() {
        return true;
      }
    
      @Override
      public boolean isTraceEnabled() {
        return true;
      }
    
      @Override
      public void error(String s, Throwable e) {
        System.err.println(s);
        e.printStackTrace(System.err);
      }
    
      @Override
      public void error(String s) {
        System.err.println(s);
      }
      // ...省略無關代碼
    }
    

    到這里起碼你知道了為什么我們通過配置 MyBatis 所用日志的具體實現 logImpl就可以實現日志輸出到控制臺的效果了。

    那么我們還可以深究一下 statementLog 是在什么時候變成 StdOutImpl的,在解析Mybatis配置文件的時候,會去讀取我們配置的logImpl屬性,然后通過LogFactory.useCustomLogging方法先指定好適配器的構造方法

    // org.apache.ibatis.builder.xml.XMLConfigBuilder#loadCustomLogImpl  
    private void loadCustomLogImpl(Properties props) {
        Class logImpl = resolveClass(props.getProperty("logImpl"));
        configuration.setLogImpl(logImpl);
    }
    
    public void setLogImpl(Class logImpl) {
        if (logImpl != null) {
            this.logImpl = logImpl;
            LogFactory.useCustomLogging(this.logImpl);
        }
    }
    

    然后在構建MappedStatement的時候就已經將日志對象初始化好了

    每個MappedStatement對應了我們自定義Mapper接口中的一個方法,它保存了開發人員編寫的SQL語句、參數結構、返回值結構、Mybatis對它的處理方式的配置等細節要素,是對一個SQL命令是什么、執行方式的完整定義。

    public Builder(Configuration configuration, String id, SqlSource sqlSource, SqlCommandType sqlCommandType) {
        // ...省略無關代碼
        mappedStatement.statementLog = LogFactory.getLog(logId);
        mappedStatement.lang = configuration.getDefaultScriptingLanguageInstance();
    }
    
    public static Log getLog(String logger) {
        try {
            return logConstructor.newInstance(logger);
        } catch (Throwable t) {
            throw new LogException("Error creating logger for logger " + logger + ".  Cause: " + t, t);
        }
    }
    

    最后SpringBoot的就不概述了

    • 第一種方式其實也是同理
    • 第二種方式是通過修改了日志級別,然后使 isDebugEnabled 返回true,去返回代理對象,然后去輸出SQL語句。

    感興趣的還可以看看SQL語句的輸出是怎么輸出的,具體在 ConnectionLogger的invoke方法中,你會發現熟悉的Preparing: "和"Parameters: "。

    logmybatis
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    Java 開發中常用的幾款日志框架有很多種,并且這些日志框架來源于不同的開源組織,給用戶暴露的接口也有很多不同之處,所以很多開源框架會自己定義一套統一的日志接口,兼容上述第三方日志框架,供上層使用。
    前言作為一個資深后端碼農天天都要和數據庫打交道,最早使用的是 Hiberate,一個封裝性極強的持久性框架。自從接觸到 Mybatis 就被它的靈活性所折服了,可以自己寫 SQL,雖然輕量級,但是麻雀雖小,五臟俱全。這篇文章就來講講什么是 Mybatis,如何簡單的使用 Mybatis
    MyBatis-Plus是一個 MyBatis 的增強工具,在 MyBatis 的基礎上只做增強不做改變,為簡化開發、提高效率而生。真實開發中,version(樂觀鎖),deleted、gmt_create、gem_mo
    文章末尾會附上文章的所有代碼、腳本和測試用例。本文環境: SpringBoot 2.5.7 + MySQL 8.0 X + MybatisPlus + Swagger2.9.2模擬工具: Jmeter模擬場景: 減庫存->創建訂單->模擬支付2.商品秒殺-超賣在開發中,對于下面的代碼,可能很熟悉:在Service里面加上@Transactional事務注解和Lock鎖控制層:Controller@ApiOperation. "哎呦喂,人也太多了,請稍后!
    16 條代碼規范
    2022-01-18 07:34:07
    如何更規范化編寫Java 代碼 Many of the happiest people are those who own the least. But are we really so happy with our IPhones, our big houses, our fancy cars? 忘川如斯,擁有一切的人才更怕失去。 背景:如何更規范化編寫Java 代碼的重要性想必毋需多言,
    前言為什么使用spring-authorization-server?真實原因:原先是因為個人原因,需要研究新版鑒權服務,看到了spring-authorization-server,使用過程中,想著能不能整合新版本cloud,因此此處先以springboot搭建spring-authorization-server,后續再替換為springcloud2021。官方原因:原先使用Spring Security OAuth,而該項目已經逐漸被淘汰,雖然網上還是有不少該方案,但秉著技術要隨時代更新,從而使用spring-authorization-serverSpring 團隊正式宣布 Spring Security OAuth 停止維護,該項目將不會再進行任何的迭代項目構建以springboot搭建spring-authorization-server數據庫相關表結構構建需要創建3張表,sql分別如下CREATE?
    但是,這樣一來,就必須在每一個Controller類都定義一套這樣的異常處理方法,因為異常可以是各種各樣。所以注解@ControllerAdvice出現了,簡單的說,該注解可以把異常處理器應用到所有控制器,而不是單個控制器。這就是統一異常處理的原理。統一異常處理實戰在定義統一異常處理類之前,先來介紹一下如何優雅的判定異常情況并拋異常。
    近日,Nagios發布了Nagios Log Server存在跨站腳本漏洞的風險通告,漏洞CVE編號為CVE-2021-35478和CVE-2021-35479。攻擊者可利用該漏洞執行惡意JavaScript代碼,實現竊取cookies或重定向用戶等攻擊。該漏洞POC已公開。目前廠商已發布安全版本修復該漏洞,建議受影響用戶及時升級到安全版本進行防護,并做好資產自查以及預防工作,以免遭受黑客攻擊。
    針對大量log日志快速定位錯誤地方動態查看日志tail?從頭打開日志文件cat?可以使用 >nanjiangtest.txt 輸出某個新日志去查看[root@yesky?tail/head簡單命令使用:[root@yesky?查詢日志尾部最后number行的日志。我是這樣做的,首先: cat -n test.log | grep “關鍵詞” 得到關鍵日志的行號[root@yesky?[11:07 17:47:11] INFO nanjiang:Edit Old Article:717892466-2020-11-07 17:47:11. 「cat -n catalina.out|tail -n +13230539|head -n 10」tail -n +13230539表示查詢13230539行之后的日志head -n 10則表示在前面的查詢結果里再查前10條記錄[root@yesky?
    360漏洞云監測到Nagios Log Server存在跨站腳本漏洞。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类