Mybatis的sql組裝詳解

2021-01-10 IT樂知

上一篇分析了SqlSession執行sql的過程,其中並沒有分析sql是從哪裡來的,今天就來仔細分析下。

Sql來源

從上一篇的最後一步執行sql那裡倒推sql的來源,源碼主要過程如下圖:

可以看到最後是通過BoundSql直接獲取的sql,然後往前倒推最後發現是通過MappedStatement的getBoundSql方法返回的。MappedStatement在之前分析mapper的時候知道一個執行sql對應一個MappedStatement對象,它封裝有mybatis中需要執行一條sql的所有信息,所以從這裡獲取也是理所應當的。

MappedStatement的getBoundSql方法

那麼就來看下MappedStatement的getBoundSql方法吧,源碼如下圖:

首先是右邊MappedStatement的getBoundSql方法,這次改了下sql傳遞了兩個參數,但是對程序基本沒什麼影響。可以看到BoundSql是通過SqlSource創建的,通過debug知道是DynamicSqlSource對象。

左邊是DynamicSqlSource的getBoundSql方法,可以先看後面的創建BoundSql對象,是通過SqlSource初始化的,而創建SqlSource對象需要的第一個參數是通過context.getSql()得到的。

而在getBoundSql方法第一步是初始化了context,從上圖的debug可以看到目前context的信息,bindings屬性已經通過初始化把參數設置進去,而sqlBuilder還是一個空字符串。關鍵代碼就在當前打斷點的「rootSqlNode.apply(context);」這行代碼。

從上圖可以看到rootSqlNode是一個MixedSqlNode對象,MixedSqlNode對象的apply方法是遍歷屬性contents的所有元素並執行它們的apply方法,可以看到contents只有一個TextSqlNode對象的元素。所以最終來到TextSqlNode對象。

TextSqlNode對象的text屬性保存的就是還沒有進行處理的sql。

TextSqlNode處理sql

TextSqlNode處理sql的源碼如下圖:

右側是TextSqlNode的apply方法,參數context就是上一步需要的context,他裡面包含有請求參數,從上一張源碼圖可以知道它的屬性sqlBuilder此時還是一個空字符串,而apply就是去拼接sqlBuilder。

apply方法首先創建了一個GenericTokenParser對象,GenericTokenParser對象有三個屬性openToken、closeToken、handler。其中openToken、closeToken分別對應字符串」${」、」}」,而handler的屬性context對應傳遞進來的context,injectionFilter屬性是TextSqlNode的injectionFilter。apply第二行代碼「context.appendSql(parser.parse(text));」可以看出來parser.parse(text)就是生成sql的代碼。其中text是TextSqlNode中沒有處理的原始sql。

所以sql生成在GenericTokenParser的parse方法,parse方法的關鍵源碼如上圖左側。主要步驟分析如下:

1、從sql找到第一個「${」位置start,獲取到元素sql(text)的字符數組src;

2、把src從開始到start處的字符拼接到結果builder上,也就是把「${」的內容拼接到結果上;

3、從start+2的位置開始找「}」的位置end,從src數組裡取出「${」、「}」之間的字符組成字符串並根據字符串從hander中取出字符串對應的參數值。並把值拼接到結果builder上。

最後還有一點代碼沒有截取到「start = text.indexOf(openToken, offset);

} while (start > -1);」也就是第4步。

4、繼續找到下一個「${」並拼接。最終組成完整的sql。

實際上GenericTokenParser的parse方法還是比較簡單的,就是把傳遞進來的參數text中openToken、closeToken中間的內容替換成對應的參數。

總結

從之前的分析我們知道一個MappedStatement對應一個sql,那麼如何從MappedStatement獲取組裝好的sql呢,實際上是MappedStatement的屬性sqlSource。

而sqlSource實際上是依靠rootSqlNode,在之前我們分析過解析mapper文件是把sql生成了嵌套的各種SqlNode子類。今天就看到他們的使用了。不過今天的算是比較簡單的,後面來一個稍微複雜一點的看看sql的解析過程。

Java程式設計師日常學習筆記,如理解有誤歡迎各位交流討論!

相關焦點

  • MyBatis 動態 SQL 詳解(以後寫 SQL 爽多了)
    -- mybatis --><dependency>  <groupId>org.mybatis</groupId>  <artifactId>mybatis</artifactId>
  • Mybatis中mapper相關註解解析類詳解
    接著上一篇通過掃描接口添加mapper的方法會創建MapperAnnotationBuilder並執行parse方法,具體源碼如下圖:MapperAnnotationBuilder關鍵屬性說明:statementAnnotationTypes:靜態屬性,存有各種sql對於在mybatis
  • Mybatis中SqlSource解析流程詳解
    前面幾篇文章都在詳細分析mapper的加載過程,但是始終沒有看到sql的解析過程,今天來詳細分析下。今天我們只關注mybatis自帶的一個實現XMLLanguageDriver。XMLScriptBuilder詳解XMLScriptBuilder類部分源碼如下圖:XMLScriptBuilder的初始化比較簡單,要記住XNode context對應的是xml中的一個select、update等節點,在最後調用了initNodeHandlerMap方法設置了
  • MyBatis dynamic SQL 1.1.4 發布,生成動態 SQL 的框架
    此版本包含兩項增強功能: 支持在計數、刪除、選擇和更新語句之間共享 where 子句 改進 Kotlin DSL - 刪除了一些引起歧義的擴展方法,並添加了自定義構建器以及一項 bug 修復: 修復了以下錯誤:在子查詢中首先使用限制/偏移/獲取會導致參數名稱衝突完整更新內容可查看:https://github.com/mybatis/mybatis-dynamic-sql
  • mybatis使用註解編寫動態sql注意事項
    在spring+springmvc+mybatis框架中開發,編寫sql語句以前都是直接使用xml的方式編寫,現在由於新的項目框架搭建過程中,同事配置的是使用註解的方式編寫sql語句,因此我也開始學習使用註解編寫sql語句。
  • Mybatis 動態sql 學習總結
    mybatis 的動態sql語句是基於OGNL表達式的。可以方便的在 sql 語句中實現某些邏輯.總體說來mybatis 動態SQL 語句主要有以下幾類:  1. if 語句 (簡單的條件判斷)  2. choose (when,otherwize) ,相當於java 語言中的 switch ,與 jstl 中的choose 很類似.
  • 比mybatis 強大優雅的 sqltoy-orm-4.11.6 發版了
    在目前有這麼多ORM框架的情況下,再搞一個開源框架的前提就是必須要比之前的好很多,而在中國如果不超過mybatis(plus)就根本沒有必要投入精力做這件事!因為大家知道開源就是在別人忙掙錢或者玩樂的時候而你卻在不計得失的奉獻!而我希望給大家奉獻一個真正有趣的有靈魂的框架!
  • TK-Mybatis[1]的Example的一些用法
    背景最近項目中使用tk-mybatis遇到了一些問題:問題詳情:(A=A1 and B=B1) or (A=A2 and
  • Mybatis 動態sql語句if和where標籤巧妙使用
    本文章描述的是 Mybatis if標籤和where標籤的結合使用需求是:根據電話和名字查用戶數據1 sql 查詢查詢sql語句如下:SELECT id , gender , nickname , mobile , avatar FROM dts_user WHERE gender = 1 AND mobile LIKE '%456%'查詢結果如下圖所示:2 mybatis動態sql配置
  • Mybatis_day01
    Mybatis通過xml或註解的方式將要執行的各種statement(statement、preparedStatemnt、CallableStatement)配置起來,並通過java對象和statement中的sql進行映射生成最終執行的sql語句,最後由mybatis框架執行sql並將結果映射成java對象並返回。
  • 建議收藏,mybatis插件原理詳解
    上次發文說到了如何集成分頁插件MyBatis插件原理分析,看完感覺自己better了,今天我們接著來聊mybatis插件的原理。插件原理分析mybatis插件涉及到的幾個類:我將以 Executor 為例,分析 MyBatis 是如何為 Executor 實例植入插件的。Executor 實例是在開啟 SqlSession 是被創建的,因此,我們從源頭進行分析。
  • Java程式設計師開發必備 MyBatis高級應用之逆向工程自動生成SQL語句
    MyBatis的一個主要的特點就是需要程式設計師自己編寫sql,那麼如果表太多的話,難免會很麻煩,所以mybatis官方提供了一個逆向工程,可以針對單表自動生成mybatis執行所需要的代碼,一般在開發中,常用的逆向工程方式是通過資料庫的表生成代碼。
  • Mybatis 中xml和註解映射,原來如此簡單
    " type=""></resultMap> <sql id=""></sql> <cache blocking="" ></cache> <cache-ref namespace=""></cache-ref> <select id="selectUserById"><
  • Spring Boot 2.X 實戰--SQL 資料庫(MyBatis)
    2、MyBatis 的配置新建項目 03-sql-mybatis,記得勾選 MyBatis 、MySQL 依賴,注意 Spring Boot 的版本要為 2.1.X 版本。DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 3<!
  • Mybatis的SqlSession創建過程詳解
    前面mybatis的初始化過程分析完成,接下來是第二步SqlSession的創建。創建過程總覽SqlSession創建過程如下圖:創建過程還是比較簡單的,首先是之前分析的SqlSessionFactory,在mybatis中提供了兩個SqlSessionFactory實現:SqlSessionManager和DefaultSqlSessionFactory
  • MyBatis JPA Extra,MyBatis JPA 擴展 v2.2 發布
    ;import org.apache.mybatis.jpa.test.domain.Students;import org.apache.mybatis.jpa.util.WebContext;import
  • Java Mybatis CRUD增刪改查
    package com.cy.dao;import com.cy.pojo.User;import java.util.List;public interface UserMapper { List<User> getUserList(); User getUserById(int id);}2、編寫對應的mapper中的sql
  • 比 mybatis 強大優雅的 sqltoy-orm-4.10.5 發版了
    文章轉載自 OSCHINA 社區 [http://www.oschina.net]本文標題:比 mybatis
  • Mybatis中mapper的xml解析詳解
    configuration.getSqlFragments()四個參數初始化一個XMLMapperBuilder;然後執行XMLMapperBuilder對象的parse方法;初始化XMLMapperBuilder初始化過程源碼已在上圖的右側部分,初始化XMLMapperBuilder方法的流程與加載mybatis
  • Mybatis初始化過程簡單總結
    前面連續多篇文章都是在數據mybatis的初始化過程,目前基本完成,是時候做一個總結了。所以SqlSessionFactory的初始化實際上是mybatis的全局配置類Configuration的初始化。而它的初始通過XMLConfigBuilder的parse方法實現。