Mybatis源碼閱讀(二):動態節點解析2.2 —— SqlSourceBuilder與三種SqlSource

*************************************優雅的分割線 **********************************

分享一波:程序員賺外快-必看的巔峰干貨

如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程

請關注微信公眾號:HB荷包
在這里插入圖片描述
一個能讓你學習技術和賺錢方法的公眾號,持續更新
*************************************優雅的分割線 **********************************

SqlSourceBuilder

前面我們對SqlSource和SqlNode進行了介紹,在經過SqlNode.apply方法的解析之后,Sql語句會被傳遞到SqlSourceBuilder中進行進一步的解析。SqlSourceBuilder主要完成了兩方面的操作,一方面是解析Sql中的#{}占位符定義的屬性,如jdbcType、javaType(使用較少),一方面是把#{}占位符替換成?占位符

SqlSourceBuilder代碼如下

/**

  • 用于進一步解析SqlSource中的${}

  • @author Clinton Begin
    */
    public class SqlSourceBuilder extends BaseBuilder {

    private static final String PARAMETER_PROPERTIES = “javaType,jdbcType,mode,numericScale,resultMap,typeHandler,jdbcTypeName”;

    public SqlSourceBuilder(Configuration configuration) {
    super(configuration);
    }

    /**

    • 解析#{}
    • @param originalSql 被SqlNode.apply解析后的sql
    • @param parameterType 參數類型
    • @param additionalParameters DynamicContext.bindings集合
    • @return
      */
      public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {
      ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);
      // 解析#{}
      GenericTokenParser parser = new GenericTokenParser("#{", “}”, handler);
      String sql = parser.parse(originalSql);
      // 最終被解析成含有?的靜態sql。創建StaticSqlSource,包含這個sql和參數
      return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
      }

    /**

    • 內部類,是解析#{}的核心
      */
      private static class ParameterMappingTokenHandler extends BaseBuilder implements TokenHandler {

      /**

      • 記錄解析后得到的parameterMapping集合
        /
        private List parameterMappings = new ArrayList<>();
        /
        *
      • 參數類型
        /
        private Class<?> parameterType;
        /
        *
      • DynamicContext.bindings對應的類
        */
        private MetaObject metaParameters;

      public ParameterMappingTokenHandler(Configuration configuration, Class<?> parameterType, Map<String, Object> additionalParameters) {
      super(configuration);
      this.parameterType = parameterType;
      this.metaParameters = configuration.newMetaObject(additionalParameters);
      }

      public List getParameterMappings() {
      return parameterMappings;
      }

      /**

      • 占位符處理器核心方法。
      • @param content
      • @return
        */
        @Override
        public String handleToken(String content) {
        parameterMappings.add(buildParameterMapping(content));
        return “?”;
        }

      private ParameterMapping buildParameterMapping(String content) {
      Map<String, String> propertiesMap = parseParameterMapping(content);
      String property = propertiesMap.get(“property”);
      Class<?> propertyType;
      if (metaParameters.hasGetter(property)) { // issue #448 get type from additional params
      propertyType = metaParameters.getGetterType(property);
      } else if (typeHandlerRegistry.hasTypeHandler(parameterType)) {
      propertyType = parameterType;
      } else if (JdbcType.CURSOR.name().equals(propertiesMap.get(“jdbcType”))) {
      propertyType = java.sql.ResultSet.class;
      } else if (property == null || Map.class.isAssignableFrom(parameterType)) {
      propertyType = Object.class;
      } else {
      MetaClass metaClass = MetaClass.forClass(parameterType, configuration.getReflectorFactory());
      if (metaClass.hasGetter(property)) {
      propertyType = metaClass.getGetterType(property);
      } else {
      propertyType = Object.class;
      }
      }
      ParameterMapping.Builder builder = new ParameterMapping.Builder(configuration, property, propertyType);
      Class<?> javaType = propertyType;
      String typeHandlerAlias = null;
      for (Map.Entry<String, String> entry : propertiesMap.entrySet()) {
      String name = entry.getKey();
      String value = entry.getValue();
      // 處理jdbcType、javaType等一大堆東西
      if (“javaType”.equals(name)) {
      javaType = resolveClass(value);
      builder.javaType(javaType);
      } else if (“jdbcType”.equals(name)) {
      builder.jdbcType(resolveJdbcType(value));
      } else if (“mode”.equals(name)) {
      builder.mode(resolveParameterMode(value));
      } else if (“numericScale”.equals(name)) {
      builder.numericScale(Integer.valueOf(value));
      } else if (“resultMap”.equals(name)) {
      builder.resultMapId(value);
      } else if (“typeHandler”.equals(name)) {
      typeHandlerAlias = value;
      } else if (“jdbcTypeName”.equals(name)) {
      builder.jdbcTypeName(value);
      } else if (“property”.equals(name)) {
      // Do Nothing
      } else if (“expression”.equals(name)) {
      throw new BuilderException(“Expression based parameters are not supported yet”);
      } else {
      throw new BuilderException(“An invalid property '” + name + “’ was found in mapping #{” + content + "}. Valid properties are " + PARAMETER_PROPERTIES);
      }
      }
      if (typeHandlerAlias != null) {
      builder.typeHandler(resolveTypeHandler(javaType, typeHandlerAlias));
      }
      return builder.build();
      }

      private Map<String, String> parseParameterMapping(String content) {
      try {
      return new ParameterExpression(content);
      } catch (BuilderException ex) {
      throw ex;
      } catch (Exception ex) {
      throw new BuilderException(“Parsing error was found in mapping #{” + content + "}. Check syntax #{property|(expression), var1=value1, var2=value2, …} ", ex);
      }
      }
      }

}

[點擊并拖拽以移動]

其中,ParameterMappingTokenHandler是SqlSourceBuilder的一個內部類,該類是解析#{}的核心。

ParameterMapping中記錄了#{}占位符中的參數屬性,字段如下

public class ParameterMapping {

private Configuration configuration;/*** 參數名*/
private String property;
/*** 參數模式。輸入參數還是輸出參數*/
private ParameterMode mode;
private Class<?> javaType = Object.class;
private JdbcType jdbcType;
private Integer numericScale;
private TypeHandler<?> typeHandler;
private String resultMapId;
private String jdbcTypeName;
private String expression;

}

之后,SqlSourceBuilder會將Sql語句以及parameterMap平時集合封裝成StaticSqlSource對象。StaticSqlSource.getBoundSql方法直接返回BoundSql,BoundSql代碼如下。

/**

  • Sql實體。包含sql和 參數集合

  • @author Clinton Begin
    */
    public class BoundSql {

    private final String sql;
    /**

    • SQL中參數屬性集合#{item}這些
      /
      private final List parameterMappings;
      /
      *
    • 執行SQL時傳入的實際參數
      /
      private final Object parameterObject;
      /
      *
    • DynamicContext.bindings集合
      /
      private final Map<String, Object> additionalParameters;
      /
      *
    • additionalParameters對應的MetaObject
      */
      private final MetaObject metaParameters;

    public BoundSql(Configuration configuration, String sql, List parameterMappings, Object parameterObject) {
    this.sql = sql;
    this.parameterMappings = parameterMappings;
    this.parameterObject = parameterObject;
    this.additionalParameters = new HashMap<>();
    this.metaParameters = configuration.newMetaObject(additionalParameters);
    }

    public String getSql() {
    return sql;
    }

    public List getParameterMappings() {
    return parameterMappings;
    }

    public Object getParameterObject() {
    return parameterObject;
    }

    public boolean hasAdditionalParameter(String name) {
    String paramName = new PropertyTokenizer(name).getName();
    return additionalParameters.containsKey(paramName);
    }

    public void setAdditionalParameter(String name, Object value) {
    metaParameters.setValue(name, value);
    }

    public Object getAdditionalParameter(String name) {
    return metaParameters.getValue(name);
    }
    }

DynamicSqlSource

DynamicSqlSource負責解析動態SQL語句,也是最常用的SqlSource實現。DynamicSqlSource使用rootSqlNode字段,記錄了帶解析的SqlNode根節點DynamicSqlSource的代碼如下。

/**

  • 負責解析動態sql語句

  • 包含#{}占位符

  • @author Clinton Begin
    */
    public class DynamicSqlSource implements SqlSource {

    private final Configuration configuration;
    private final SqlNode rootSqlNode;

    public DynamicSqlSource(Configuration configuration, SqlNode rootSqlNode) {
    this.configuration = configuration;
    this.rootSqlNode = rootSqlNode;
    }

    @Override
    public BoundSql getBoundSql(Object parameterObject) {
    // 創建
    DynamicContext context = new DynamicContext(configuration, parameterObject);
    // 解析sql節點
    rootSqlNode.apply(context);
    SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
    Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
    // 解析sql,將#{}替換成?
    SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    context.getBindings().forEach(boundSql::setAdditionalParameter);
    return boundSql;
    }

}

RawSqlSource

RawSqlSource的邏輯和DynamicSqlSource類似,但是處理SQL的機制不同。RawSqlSource用于處理非動態SQL。當一個sql中只包含#{}占位符。不包含${}和動態sql節點,就不是動態SQL語句,會創建相應的StaticTextSqlNode在XmlScriptBuilder.parseScriptNode方法會判斷整個SQL節點是否是動態的,如果是動態,就用DynamicSqlSource進行處理,否則用RawSqlSource進行處理。

RawSqlSource在構造方法中會調用getSql方法,該方法會調用SqlNode.apply方法完成sql語句的處不處理。SqlSourceBuilder完成占位符的替換,并返回StaticSqlSource對象。

/**

  • 處理非動態sql語句

  • 如果節點只包含“#{}”占位符,而不包含動態 SQL 點或未解析的 “${}”占位

  • 符的話, 則不是動態 SQL 語句

  • @author Eduardo Macarron

  • @since 3.2.0
    */
    public class RawSqlSource implements SqlSource {

    private final SqlSource sqlSource;

    public RawSqlSource(Configuration configuration, SqlNode rootSqlNode, Class<?> parameterType) {
    this(configuration, getSql(configuration, rootSqlNode), parameterType);
    }

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

    private static String getSql(Configuration configuration, SqlNode rootSqlNode) {
    DynamicContext context = new DynamicContext(configuration, null);
    rootSqlNode.apply(context);
    return context.getSql();
    }

    @Override
    public BoundSql getBoundSql(Object parameterObject) {
    return sqlSource.getBoundSql(parameterObject);
    }
    }

像foreach、if、where等標簽,以及${}占位符,在mybatis初始化時并不知道其具體含義,因此這類sql就視為“動態sql”,交由DynamicSqlSource在程序運行時進行解析。而如果只含有#{}占位符,則會在mybatis初始化時就完成sql解析。
*************************************優雅的分割線 **********************************

分享一波:程序員賺外快-必看的巔峰干貨

如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程

請關注微信公眾號:HB荷包
在這里插入圖片描述
一個能讓你學習技術和賺錢方法的公眾號,持續更新

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:
http://www.pswp.cn/news/536978.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/536978.shtml
英文地址,請注明出處:http://en.pswp.cn/news/536978.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

搞懂toString()與valueOf()的區別

一、toString&#xff08;&#xff09; 作用&#xff1a;toString&#xff08;&#xff09;方法返回一個表示改對象的字符串&#xff0c;如果是對象會返回&#xff0c;toString() 返回 “[object type]”,其中type是對象類型。 二、valueOf( ) 作用&#xff1a;valueOf房啊發返…

oracle入庫的速度能到多少_倒車入庫別練復雜了,其實就這兩點

教練總會讓學員反復練倒車入庫&#xff0c;但不少學員都會有這樣的疑惑&#xff1a;為什么每一次倒庫結果都不一樣&#xff0c;倒車入庫的練習重點是什么&#xff1f;倒車入庫是科二的重點及難點&#xff0c;但只要掌握以下兩個關鍵&#xff0c;順利通過真不難&#xff1a;01方…

Mybatis源碼閱讀(三):結果集映射3.1 —— ResultSetBuilder與簡單映射

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

kdj買賣指標公式源碼_通達信指標公式源碼MACD背離KDJ背離指標

N1:5;N2:10;N3:21;N4:60;牛熊:EMA(CLOSE,N4),COLORGREEN,LINETHICK3;DIFF:EMA(CLOSE,12) - EMA(CLOSE,26);DEA:EMA(DIFF,8);A1:BARSLAST(REF(CROSS(DIFF,DEA),1)); B1:REF(C,A11)>C AND REF(DIFF,A11)DRAWTEXT(IF(B1>0,1,0),L-0.1,MACD底背),COLORGREEN;RSV:(CLOSE-LLV(L…

Mybatis源碼閱讀(三):結果集映射3.2 —— 嵌套映射

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

18.requests

多多的的轉載于:https://www.cnblogs.com/yangyangchunchun/p/10368337.html

gridview獲取選中行數據_Word轉Excel,不想熬夜加班,那就掌握這個數據清洗方法...

私信回復關鍵詞【福利】~獲取豐富辦公資源&#xff0c;助你高效辦公早下班&#xff01;小伙伴們&#xff0c;大家好&#xff0c;我是專治各種疑難雜「數」的農夫~今天&#xff0c;我就為大家介紹一種高效的數據清洗方法&#xff0c;助你告別熬夜加班&#xff0c;擁抱美好的夜晚…

如何深入學習python_菜鳥如何學好python

python在我國發展得如火如荼&#xff0c;因其操作簡單&#xff0c;應用廣泛受到很多人的喜歡。下面小編就來說說菜鳥如何學好python&#xff0c;一起來看看吧!1. 了解編程的基礎知識種是變量、編程規范、基本語法等&#xff0c;這也是開始編寫Python代碼的先決條件。第二種是數…

HTML5中本地儲存概念是什么,什么優點 ,與cookie有什么區別?

html5中的Web Storage 包括了兩種存儲方式&#xff1a; sessionStorage 和 localStorage. seessionStorage 用于本地存儲一個會話&#xff08;session&#xff09;中的數據&#xff0c;這些數據只有在同一個會話中的頁面才能訪問并且當會話結束后數據也隨之銷毀。因此session…

Mybatis源碼閱讀(三):結果集映射3.3 —— 主鍵生成策略

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

list最大容量_Java 基礎(四)集合源碼解析 List

List 接口前面我們學習了Iterator、Collection&#xff0c;為集合的學習打下了基礎&#xff0c;現在我們來學習集合的第一大體系 List。List 是一個接口&#xff0c;定義了一組元素是有序的、可重復的集合。List 繼承自 Collection&#xff0c;較之 Collection&#xff0c;List…

Mybatis源碼閱讀(四):核心接口4.1——StatementHandler

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

Shell學習之結合正則表達式與通配符的使用(五)

Shell學習之結合正則表達式與通配符的使用 目錄 通配符 正則表達式與通配符通配符通配符的使用正則表達式 正則表達式正則表達式的使用通配符 正則表達式與通配符 正則表達式用來在文件中匹配符合條件的字符串&#xff0c;正則是包含匹配。grep、awk、sed等命令可以支持正則表達…

Mybatis源碼閱讀(四):核心接口4.2——Executor(上)

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

接收xml參數_SpringBoot實戰(二):接收xml請求

強烈推薦一個大神的人工智能的教程&#xff1a;http://www.captainbed.net/zhanghan【前言】最近在對接一個第三方系統&#xff0c;需要接收第三方系統的回調&#xff0c;而且格式為XML形式&#xff0c;之前自己一般接收的參數是Json形式&#xff0c;于是乎做個實驗驗證一下使用…

報錯 插入更新_window如何解決mysql數據量過大導致的報錯

window如何解決報錯“The total number of locks exceeds the lock table size”第一大步&#xff0c;查看mysql配置信息在CMD中輸入mysql -hlocalhost -uroot -p #如果設置了密碼直接接在p 后面 show variables like %storage_engine%以下為結果可以看到InnoDB是MySQL的默認引…

148. Sort List

Sort a linked list in O(n log n) time using constant space complexity. Example 1: Input: 4->2->1->3 Output: 1->2->3->4 Example 2: Input: -1->5->3->4->0 Output: -1->0->3->4->5難度&#xff1a;medium 題目&#xff1a;排…

Mybatis源碼閱讀(四):核心接口4.2——Executor(下)

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

python解橢圓方程的例題_橢圓標準方程典型例題及練習題

橢圓標準方程典型例題例1已知P 點在以坐標軸為對稱軸的橢圓上&#xff0c;點P 到兩焦點的距離分別為354和352&#xff0c;過P 點作焦點所在軸的垂線&#xff0c;它恰好過橢圓的一個焦點&#xff0c;求橢圓方程&#xff0e; 解&#xff1a;設兩焦點為1F 、2F &#xff0c;且3541…

leetcode393. UTF-8 Validation

題目要求 A character in UTF8 can be from 1 to 4 bytes long, subjected to the following rules:For 1-byte character, the first bit is a 0, followed by its unicode code. For n-bytes character, the first n-bits are all ones, the n1 bit is 0, followed by n-1 by…