1. 程式人生 > >Mybatis系列全解(八):Mybatis的9大動態SQL標籤你知道幾個?提前致女神!

Mybatis系列全解(八):Mybatis的9大動態SQL標籤你知道幾個?提前致女神!

> 封面:洛小汐 > > 作者:潘潘 ![](https://pic2.zhimg.com/v2-7eec74a18a3d77f5008c04f97b283910_r.jpg) ![](https://gitee.com/senlypan/notes/raw/master/images/sourceMaterial/slogan_start.png) 2021年,仰望天空,腳踏實地。 ![](https://gitee.com/senlypan/notes/raw/master/images/sourceMaterial/slogan_end.png) ![](https://gitee.com/senlypan/notes/raw/master/images/Mybatis/project8/wechat_newyear.png) > 這算是春節後首篇 Mybatis 文了~ > > 跨了個年感覺寫了有半個世紀 ... > > 藉著女神節 ヾ(◍°∇°◍)ノ゙ > > 提前祝男神女神們越靚越富越嗨森! > > 上圖儲存可做朋友圈封面圖 ~ ### 前言 本節我們介紹 Mybatis 的強大特性之一:**動態 SQL** ,從動態 SQL 的誕生背景與基礎概念,到動態 SQL 的標籤成員及基本用法,我們徐徐道來,再結合框架原始碼,剖析動態 SQL (標籤)的底層原理,最終在文末吐槽一下:在無動態 SQL 特性(標籤)之前,我們會常常掉進哪些可惡的坑吧~ ![](https://gitee.com/senlypan/notes/raw/master/images/Mybatis/project8/d1.png) **建議關注我們! Mybatis 全解系列一直在更新哦** ![](https://gitee.com/senlypan/notes/raw/master/images/Mybatis/mybatis_all.jpg) ### Mybaits系列全解 - Mybatis系列全解(一):手寫一套持久層框架 - Mybatis系列全解(二):Mybatis簡介與環境搭建 - Mybatis系列全解(三):Mybatis簡單CRUD使用介紹 - Mybatis系列全解(四):全網最全!Mybatis配置檔案XML全貌詳解 - Mybatis系列全解(五):全網最全!詳解Mybatis的Mapper對映檔案 - Mybatis系列全解(六):Mybatis最硬核的API你知道幾個? - Mybatis系列全解(七):Dao層的兩種實現之傳統與代理 - Mybatis系列全解(八):Mybatis的動態SQL - Mybatis系列全解(九):Mybatis的複雜對映 - Mybatis系列全解(十):Mybatis註解開發 - Mybatis系列全解(十一):Mybatis快取全解 - Mybatis系列全解(十二):Mybatis外掛開發 - Mybatis系列全解(十三):Mybatis程式碼生成器 - Mybatis系列全解(十四):Spring整合Mybatis - Mybatis系列全解(十五):SpringBoot整合Mybatis - Mybatis系列全解(十六):Mybatis原始碼剖析 ### 本文目錄 **1、什麼是動態SQL** **2、動態SQL的誕生記** **3、動態SQL標籤的9大標籤** **4、動態SQL的底層原理** ![](https://gitee.com/senlypan/notes/raw/master/images/sourceMaterial/01.png) #### 1、什麼是動態SQL ? 關於動態 SQL ,允許我們理解為 “ **動態的 SQL** ”,其中 “ 動態的 ” 是形容詞,“ SQL ” 是名詞,那顯然我們需要先理解名詞,畢竟形容詞僅僅代表它的某種形態或者某種狀態。 SQL 的全稱是: > Structured Query Language,結構化查詢語言。 SQL 本身好說,我們小學時候都學習過了,無非就是 CRUD 嘛,而且我們還知道它是一種 **語言**,語言是一種存在於物件之間用於交流表達的 **能力**,例如跟中國人交流用漢語、跟英國人交流用英語、跟火星人交流用火星語、跟小貓交流用喵喵語、跟計算機交流我們用機器語言、跟資料庫管理系統(DBMS)交流我們用 SQL。 ![](https://gitee.com/senlypan/notes/raw/master/images/Mybatis/project8/sql_talk.png) 想必大家立馬就能明白,想要與某個物件交流,必須擁有與此物件交流的語言能力才行!所以無論是技術人員、還是應用程式系統、或是某個高階語言環境,想要訪問/操作資料庫,都必須具備 SQL 這項能力;因此你能看到像 Java ,像 Python ,像 Go 等等這些高階語言環境中,都會嵌入(支援) SQL 能力,達到與資料庫互動的目的。 ![](https://gitee.com/senlypan/notes/raw/master/images/Mybatis/project8/sql_connect.png) 很顯然,能夠學習 Mybatis 這麼一門高精尖(ru-men)持久層框架的程式設計人群,對於 SQL 的編寫能力肯定已經掌握得 ss 的,平時各種 SQL 編寫那都是信手拈來的事, 只不過對於 **動態SQL** 到底是個什麼東西,似乎還有一些朋友似懂非懂!但是沒關係,我們百度一下。 > 動態 SQL:一般指根據使用者輸入或外部條件 **動態組合** 的 SQL 語句塊。 很容易理解,隨外部條件動態組合的 SQL 語句塊!我們先針對動態 SQL 這個詞來剖析,世間萬物,有動態那就相對應的有靜態,那麼他們的邊界在哪裡呢?又該怎麼區分呢? ![](https://gitee.com/senlypan/notes/raw/master/images/Mybatis/project8/dong_jing.jpg) 其實,上面我們已經介紹過,在例如 Java 高階語言中,都會嵌入(支援)SQL 能力,一般我們可以直接在程式碼或配置檔案中編寫 SQL 語句,如果一個 SQL 語句在 “編譯階段” 就已經能確定 **主體結構**,那我們稱之為靜態 SQL,如果一個 SQL 語句在編譯階段無法確定主體結構,需要等到程式真正 “執行時” 才能最終確定,那麼我們稱之為動態 SQL,舉個例子: ```xml ``` ```java // 2、執行SQL sqlSession.select("dao.selectAll"); ``` 很明顯,以上這個 SQL ,在編譯階段我們都已經知道它的主體結構,即查詢 t_user 表的所有記錄,而無需等到程式執行時才確定這個主體結構,因此以上屬於 **靜態 SQL**。那我們再看看下面這個語句: ```xml ``` ```java // 2、執行SQL User user1 = new User(); user1.setId(1); sqlSession.select("dao.selectAll",user1); // 有 id User user2 = new User(); sqlSession.select("dao.selectAll",user2); // 無 id ``` 認真觀察,以上這個 SQL 語句,額外添加了一塊 **if 標籤** 作為條件判斷,所以應用程式在編譯階段是無法確定 SQL 語句最終主體結構的,只有在執行時根據應用程式是否傳入 id 這個條件,來動態的拼接最終執行的 SQL 語句,因此屬於動態 SQL 。 ![](https://gitee.com/senlypan/notes/raw/master/images/Mybatis/project8/sql_process.png) 另外,還有一種常見的情況,大家看看下面這個 SQL 語句算是動態 SQL 語句嗎? ```xml ``` ```java // 2、執行SQL User user1 = new User(); user1.setId(1); sqlSession.select("dao.selectAll",user1); // 有 id ``` 根據動態 SQL 的定義,大家是否能判斷以上的語句塊是否屬於動態 SQL? **答案:不屬於動態 SQL !** 原因很簡單,這個 SQL 在編譯階段就已經明確主體結構了,雖然外部動態的傳入一個 id ,可能是1,可能是2,可能是100,但是因為它的主體結構已經確定,這個語句就是查詢一個指定 id 的使用者記錄,它最終執行的 SQL 語句不會有任何動態的變化,所以頂多算是一個支援動態傳參的靜態 SQL 。 至此,我們對於動態 SQL 和靜態 SQL 的區別已經有了一個基礎認知,但是有些好奇的朋友又會思考另一個問題:動態 SQL 是 Mybatis 獨有的嗎? ![](https://gitee.com/senlypan/notes/raw/master/images/Mybatis/project8/thinking.png) ![](https://gitee.com/senlypan/notes/raw/master/images/sourceMaterial/02.png) #### 2、動態SQL的誕生記 我們都知道,SQL 是一種偉大的資料庫語言 **標準**,在資料庫管理系統紛爭的時代,它的出現統一規範了資料庫操作語言,而此時,市面上的資料庫管理軟體百花齊放,我最早使用的 SQL Server 資料庫,當時用的資料庫管理工具是 SQL Server Management Studio,後來接觸 Oracle 資料庫,用了 PL/SQL Developer,再後來直至今日就幾乎都在用 MySQL 資料庫(這個跟各種雲廠商崛起有關),所以基本使用 Navicat 作為資料庫管理工具,當然如今市面上還有許多許多,資料庫管理工具嘛,只要能便捷高效的管理我們的資料庫,那就是好工具,duck 不必糾結選擇哪一款! ![](https://gitee.com/senlypan/notes/raw/master/images/Mybatis/project8/dbphoto.jpg) 那這麼多好工具,都提供什麼功能呢?相信我們平時接觸最多的就是接收執行 SQL 語句的輸入介面(也稱為查詢編輯器),這個輸入介面幾乎支援所有 SQL 語法,例如我們編寫一條語句查詢 id 等於15 的使用者資料記錄: ```sql select * from user where id = 15 ; ``` 我們來看一下這個查詢結果: ![](https://gitee.com/senlypan/notes/raw/master/images/Mybatis/project8/mysql_navicate.jpg) 很顯然,在這個輸入介面內輸入的任何 SQL 語句,對於資料庫管理工具來說,都是 **動態 SQL**!因為工具本身並不可能提前知道使用者會輸入什麼 SQL 語句,只有當用戶執行之後,工具才接收到使用者實際輸入的 SQL 語句,才能最終確定 SQL 語句的主體結構,當然!即使我們不通過視覺化的資料庫管理工具,也可以用資料庫本身自帶支援的命令列工具來執行 SQL 語句。但無論使用者使用哪類工具,輸入的語句都會被工具認為是 **動態 SQL**! ![](https://gitee.com/senlypan/notes/raw/master/images/Mybatis/project8/tools_sql.jpg) 這麼一說,動態 SQL 原來不是 Mybatis 獨有的特性!其實除了以上介紹的資料庫管理工具以外,在純 JDBC 時代,我們就經常通過字串來動態的拼接 SQL 語句,這也是在高階語言環境(例如 Java 語言程式設計環境)中早期常用的動態 SQL 構建方式! ```java // 外部條件id Integer id = Integer.valueOf(15); // 動態拼接SQL StringBuilder sql = new StringBuilder(); sql.append(" select * "); sql.append(" from user "); // 根據外部條件id動態拼接SQL if ( null != id ){ sql.append(" where id = " + id); } // 執行語句 connection.prepareStatement(sql); ``` 只不過,這種構建動態 SQL 的方式,存在很大的安全問題和異常風險(我們第5點會詳細介紹),所以不建議使用,後來 Mybatis 入世之後,在對待動態 SQL 這件事上,就格外上心,它默默發誓,一定要為使用 Mybatis 框架的使用者提供一套棒棒的方案(標籤)來靈活構建動態 SQL! ![](https://gitee.com/senlypan/notes/raw/master/images/Mybatis/project8/sql_dongtai.jpg) 於是乎,Mybatis 藉助 OGNL 的表示式的偉大設計,可算在動態 SQL 構建方面提供了各類功能強大的輔助標籤,我們簡單列舉一下有:if、choose、when、otherwise、trim、where、set、foreach、bind等,我隨手翻了翻我電腦裡頭曾經儲存的學習筆記,我們一起在第3節中溫故知新,詳細的講一講吧~ ![](https://gitee.com/senlypan/notes/raw/master/images/Mybatis/project8/sql_all.jpg) 另外,需要糾正一點,就是我們平日裡在 Mybatis 框架中常說的動態 SQL ,其實特指的也就是 Mybatis 框架中的這一套動態 SQL **標籤**,或者說是這一 **特性**,而並不是在說動態 SQL 本身。 ![](https://gitee.com/senlypan/notes/raw/master/images/sourceMaterial/03.png) #### 3、動態SQL標籤的9大標籤 很好,可算進入我們動態 SQL 標籤的主題,根據前面的鋪墊,其實我們都能發現,很多時候靜態 SQL 語句並不能滿足我們複雜的業務場景需求,所以我們需要有適當靈活的一套方式或者能力,來便捷高效的構建動態 SQL 語句,去匹配我們動態變化的業務需求。舉個栗子,在下面此類多條件的場景需求之下,動態 SQL 語句就顯得尤為重要(先登場 if 標籤)。 ![](https://gitee.com/senlypan/notes/raw/master/images/Mybatis/project8/es_sql.jpg) 當然,很多朋友會說這類需求,不能用 SQL 來查,得用搜索引擎,確實如此。但是呢,在我們的實際業務需求當中,還是存在很多沒有引入搜尋引擎系統,或者有些根本無需引入搜尋引擎的應用程式或功能,它們也會涉及到多選項多條件或者多結果的業務需求,那此時也就確實需要使用動態 SQL 標籤來靈活構建執行語句。 那麼, Mybatis 目前都提供了哪些棒棒的動態 SQL 標籤呢 ?我們先引出一個類叫做 XMLScriptBuilder ,大家先簡單理解它是負責解析我們的動態 SQL 標籤的這麼一個構建器,在第4點底層原理中我們再詳細介紹。 ```java // XML指令碼標籤構建器 public class XMLScriptBuilder{ // 標籤節點處理器池 private f