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 JPA Extra,MyBatis JPA 擴展 v2.2 發布
    ;import org.apache.mybatis.jpa.test.domain.Students;import org.apache.mybatis.jpa.util.WebContext;import
  • Mybatis的SqlSession創建過程詳解
    前面mybatis的初始化過程分析完成,接下來是第二步SqlSession的創建。創建過程總覽SqlSession創建過程如下圖:創建過程還是比較簡單的,首先是之前分析的SqlSessionFactory,在mybatis中提供了兩個SqlSessionFactory實現:SqlSessionManager和DefaultSqlSessionFactory
  • Mybatis初始化過程簡單總結
    前面連續多篇文章都是在數據mybatis的初始化過程,目前基本完成,是時候做一個總結了。所以SqlSessionFactory的初始化實際上是mybatis的全局配置類Configuration的初始化。而它的初始通過XMLConfigBuilder的parse方法實現。
  • BeetlSQL 3.1.2 發布,Java 資料庫訪問工具
    >JMHMain.beetlsqlOne2Many thrpt 5 108.132 ± 10.934 ops/msJMHMain.beetlsqlPageQuery thrpt 5 102.402 ± 11.129 ops/msJMHMain.mybatisExecuteTemplate
  • 從0 開始手寫一個 Mybatis 框架,三步搞定!
    我們對上圖進行分析總結:1、mybatis的配置文件有2類mybatisconfig.xml,配置文件的名稱不是固定的,配置了全局的參數的配置,全局只能有一個配置文件。Mapper.xml 配置多個statemement,也就是多個sql,整個mybatis框架中可以有多個Mappe.xml配置文件。
  • sqltoy-orm 4.13.8 發版 - OSCHINA - 中文開源技術交流社區
    ,目前以sql id為籤名,便於運維通過資料庫來跟蹤sql的歸屬以mysql為例: 在sql開始部位增加了id,便於運維通過資料庫日誌快速定位問題sql的歸屬```sql/* id=qstart_order_search */  select  ORDER_ID,      DEVICE_TYPE from xxx```感謝網名cmd的用戶反饋此意見
  • sqltoy-orm-4.17.6 發版,支持 Greenplum、並行查詢可設置並行數量
    )public <T> List<QueryResult<T>> parallQuery(List<ParallQuery> parallQueryList, Map<String, Object> paramsMap,            ParallelConfig parallelConfig);sqltoy
  • 徹底搞懂MyBatis插件原理及PageHelper原理
    package com.lonelyWolf.mybatis.plugin;import org.apache.ibatis.executor.Executor;import org.apache.ibatis.mapping.MappedStatement;import
  • sqltoy-orm-4.16.11 發版,部分功能優化
    的十四個關鍵特點:1、最簡最直觀的sql編寫方式(不僅僅是查詢語句),採用條件參數前置處理規整法,讓sql語句部分跟客戶端保持高度一致2、sql中支持注釋(規避了對hint特性的影響,知道hint嗎?搜oracle hint),和動態更新加載,便於開發和後期維護整個過程的管理3、支持緩存翻譯和反向緩存條件檢索(通過緩存將名稱匹配成精確的key),實現sql簡化和性能大幅提升4、支持快速分頁和分頁優化功能,實現分頁最高級別的優化,同時還考慮到了cte多個with as情況下的優化支持5、支持並行查詢6、根本杜絕sql注入問題,以後不需要討論這個話題7、支持行列轉換
  • sqltoy-orm 4.13.12.1 發版,增加 DTO 和 POJO 互轉工具方法
    一路走來,我只是用我的努力將大家好的idea融合在sqltoy中,希望看到這篇文章的讀者能夠真正的了解sqltoy並幫助到你們! 請不要見xml就反,說實在的在2010年之前crud就已經是沒有sql和xml了(如果有興趣可以了解一下sqltoy的update跟其它ORM的異同,也可以了解一下sqltoy中什麼是updateFetch,相信會看到另類的思考),不提crud是默認大家已經跨越了crud已經面對複雜查詢問題(難道默認值設高了?)
  • 極致性能 sqltoy-orm-4.12.9 發版 - OSCHINA - 中文開源技術交流...
    具有JPA模式的CRUD功能(即CRUD無需寫sql),無需寫Dao,sqltoy提供了SqlToyLazyDao,同時提供了quickvo從資料庫生成POJO。 根本上杜絕了sql注入問題 最科學的sql編寫方式* sqltoy的sql編寫(支持嵌套)select *from sqltoy_device_order_info t where #[
  • 極致查詢性能 sqltoy-orm-4.12.8 發版增加對國產達夢資料庫支持
    特點說明: 支持mysql、postgresql、db2、oracle、sqlserver、sqlite、clickhouse、elasticsearch、mongodb等 具有JPA模式的CRUD功能(即CRUD無需寫sql),無需寫Dao,sqltoy提供了SqlToyLazyDao,同時提供了
  • sqltoy-orm-4.16.16 發版,並行查詢場景增強、級聯增加排序
    對spring.sqltoy 配置的校驗,避免有開發者寫成sqltoy.xxx遺漏掉spring前綴7、sqltoy中增強sqlResourcesDir 格式的交易,防止開發者寫成文件匹配表達式8、強化未匹配到sql的sqlId執行錯誤提示,避免開發者遇到錯誤時無法定位錯誤9、updateByQuery增加統一欄位處理,針對最後修改時間、最後修改人等公共欄位
  • SpringBoot + MyBatis + MySQL讀寫分離實踐!
    <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot
  • BeetlSQL 3.0.0-M1 第一個版本發布 - OSCHINA - 中文開源技術交流...
    BeetlSQL3 能最大程度提高開發資料庫訪問的效率和增強相關代碼維護性BeetlSQL解決了很多資料庫訪問工具的不足 開發效率低,如mybatis,還需要搭配plus工具才能提高開發效率,而JOOQ這樣的又不適合複雜訪問 無SQL管理,遇到複雜的sql特別難維護,比如在Java裡拼寫sql,遇到調整就麻煩 跨資料庫平臺,即使
  • MyBatis-Plus為啥這麼牛?
    建議開發測試時啟用該功能,能快速揪出慢查詢內置全局攔截插件:提供全表 delete 、 update 操作智能分析阻斷,也可自定義攔截規則,預防誤操作正文在實際項目開發中,我們常常有把數據批量保存到資料庫的需求,大家或多或少的用mybatis-plus
  • springboot整合mybatis如何進行單元測試
    前言:之前使用的SSM框架(spring,springmvc,mybatis),整合時需要創建各種配置文件進行各種配置,有時感覺挺繁瑣的,之後開發中使用到了springboot,感覺真的是節約了很多的時間,整合其他框架時,明顯效率高了很多。
  • 小學妹問:Mybatis常見註解有哪些?
    org.mybatis.spring.annotation.MapperScan使用方式@SpringBootApplication@MapperScan("com.tian.mybatis.mapper")public class Application {}