Mybatis源碼解析-BoundSql


mybatis作為持久層,其操作數據庫離不開sql語句。而BoundSql則是其保存Sql語句的對象

前提

  1. 針對mybatis的配置文件的節點解析,比如where/if/trim的節點解析可見文章Spring mybatis源碼篇章-NodeHandler實現類具體解析保存Dynamic sql節點信息

  2. 針對mybatis配置文件的解析幫助類SqlSource[一般為DynamicSqlSource]的使用可見文章Spring mybatis源碼篇章-XMLLanguageDriver解析sql包裝為SqlSource

  3. 對BoundSql對象的調用獲取可見文章Mybatis源碼分析-BaseExecutor

本文將在上述的知識前提下展開對Sql語句的解析

BoundSql的引用

主要是通過MappedStatement#getBoundSql()方法調用獲取的。我們可以簡單跟蹤下其中的源碼,如下

  public BoundSql getBoundSql(Object parameterObject) {
    // 通過SqlSource獲取BoundSql對象
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    // 校驗當前的sql語句有無綁定parameterMapping屬性
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings == null || parameterMappings.isEmpty()) {
      boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
    }

    // check for nested result maps in parameter mappings (issue #30)
    for (ParameterMapping pm : boundSql.getParameterMappings()) {
      String rmId = pm.getResultMapId();
      if (rmId != null) {
        ResultMap rm = configuration.getResultMap(rmId);
        if (rm != null) {
          hasNestedResultMaps |= rm.hasNestedResultMaps();
        }
      }
    }

    return boundSql;
  }

RawSqlSource-常用的mybatis解析sql幫助類

我們觀察下其getBoundSql()方法,源碼如下

  public BoundSql getBoundSql(Object parameterObject) {
    //此處的sqlSource為RawSqlSource的內部屬性
    return sqlSource.getBoundSql(parameterObject);
  }

我們看下sqlSource是如何生成的,由此觀察其構造函數

public RawSqlSource(Configuration configuration, String sql, Class<?> parameterType) {
   // 通過SqlSourceBuilder來創建sqlSource
    SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
    Class<?> clazz = parameterType == null ? Object.class : parameterType;
    sqlSource = sqlSourceParser.parse(sql, clazz, new HashMap<String, Object>());
  }

#{}的使用這里稍微提下,一般的寫法都為{name,jdbcType=String,mode=out,javaType=java.lang.String...},其中jdbcType也可以不指定,系統會自動識別。上述的代碼其實主要就是針對#{}字符內容的處理

注意:${}這樣的字符是通過DynamicSqlSource來完成解析的,具體的解析讀者可自行分析

我們可以繼續看下SqlSourceBuilder類是如何解析獲取sql語句的

SqlSourceBuilder#parse()

直接上源碼

public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {
    // 對#{}這樣的字符串內容的解析處理類
    ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);
    GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
    // 獲取真實的可執行性的sql語句
    String sql = parser.parse(originalSql);
    // 包裝成StaticSqlSource返回
    return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
  }

簡單的看下ParameterMappingTokenHandler是如何解析的,其是TokenHandler接口的實現類,我們就關注實現方法handleToken

@Override
    public String handleToken(String content) {
      // 此處的作用就是對`#{}`節點中的key值保存映射,比如javaType/jdbcType/mode等信息,限於篇幅過長,讀者可自行分析          
      parameterMappings.add(buildParameterMapping(content));
      // 將`#{}`替換為?,即一般包裝成`select * form test where name=? and age=?`預表達式語句
      return "?";
    }

上述主要通過ParameterMappingTokenHandler類來完成對#{}字符串的解析,其中的映射信息則保存至BoundSql的parameterMappings屬性中

總結

  1. BoundSql語句的解析主要是通過對#{}字符的解析,將其替換成?。最后均包裝成預表達式供PrepareStatement調用執行

  2. #{}中的key屬性以及相應的參數映射,比如javaType、jdbcType等信息均保存至BoundSql的parameterMappings屬性中供最后的預表達式對象PrepareStatement賦值使用


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM