Mybatis-Plus的應(yīng)用場景描述及注入SQL原理分析
MyBatis-Plus(簡稱 MP)是一個 MyBatis 的增強(qiáng)工具,在 MyBatis 的基礎(chǔ)上只做增強(qiáng)不做改變,為簡化開發(fā)、提高效率而生。
愿景是成為 MyBatis 最好的搭檔,就像 魂斗羅 中的 1P、2P,基友搭配,效率翻倍。
特性:
- 無侵入:只做增強(qiáng)不做改變,引入它不會對現(xiàn)有工程產(chǎn)生影響,如絲般順滑
- 損耗?。簡蛹磿詣幼⑷牖?CURD,性能基本無損耗,直接面向?qū)ο蟛僮?/li>
- 強(qiáng)大的 CRUD 操作:內(nèi)置通用 Mapper、通用 Service,僅僅通過少量配置即可實現(xiàn)單表大部分 CRUD 操作,更有強(qiáng)大的條件構(gòu)造器,滿足各類使用需求
- 支持 Lambda 形式調(diào)用:通過 Lambda 表達(dá)式,方便的編寫各類查詢條件,無需再擔(dān)心字段寫錯
- 支持主鍵自動生成:支持多達(dá) 4 種主鍵策略(內(nèi)含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解決主鍵問題
- 支持 ActiveRecord 模式:支持 ActiveRecord 形式調(diào)用,實體類只需繼承 Model 類即可進(jìn)行強(qiáng)大的 CRUD 操作
- 支持自定義全局通用操作:支持全局通用方法注入( Write once, use anywhere )
- 內(nèi)置代碼生成器:采用代碼或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 層代碼,支持模板引擎,更有超多自定義配置等您來使用
- 內(nèi)置分頁插件:基于 MyBatis 物理分頁,開發(fā)者無需關(guān)心具體操作,配置好插件之后,寫分頁等同于普通 List 查詢
- 分頁插件支持多種數(shù)據(jù)庫:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多種數(shù)據(jù)庫
- 內(nèi)置性能分析插件:可輸出 Sql 語句以及其執(zhí)行時間,建議開發(fā)測試時啟用該功能,能快速揪出慢查詢
- 內(nèi)置全局?jǐn)r截插件:提供全表 delete 、 update 操作智能分析阻斷,也可自定義攔截規(guī)則,預(yù)防誤操作
支持?jǐn)?shù)據(jù)庫:
- mysql 、 mariadb 、 oracle 、 db2 、 h2 、 hsql 、 sqlite 、 postgresql 、 sqlserver 、 presto
- 達(dá)夢數(shù)據(jù)庫 、 虛谷數(shù)據(jù)庫 、 人大金倉數(shù)據(jù)庫
一、背景
1.1 傳統(tǒng)Mybatis的弊端
1.1.1 場景描述
假設(shè)有兩張表:一張商品表、一張訂單表,具體表的字段如下:
現(xiàn)有如下需求:
- 分別根據(jù)id查詢商品表和訂單表所有信息
- 根據(jù)支付狀態(tài)和通知狀態(tài)查詢訂單表信息
- 對訂單表增加一個訂單狀態(tài),根據(jù)訂單狀態(tài)查詢訂單信息
1.1.2 需求
需求a:根據(jù)id查詢商品表:
@Select(" SELECT p.id ,p.name ,p.picture ,p.type ,p.price, p.type, p.time FROM product p where id = #{id} ") List<Product> getProductsBYId(@Param("id") Integer id);
根據(jù)id查詢訂單表所有信息:
@Select(" SELECT o.id ,o.pay_no ,o.user_id ,o.product_id ,o.pay_price, o.num, o.pay_time, o.order_type, o.notif_type FROM order o where id = #{id} ") List<Order> getOrderBYId(@Param("id") Integer id);
需求b:根據(jù)支付狀態(tài)和通知狀態(tài)查詢訂單表信息
@Select(" SELECT o.id ,o.pay_no ,o.user_id ,o.product_id ,o.pay_price, o.num, o.pay_time, o.order_type, o.notif_type FROM order o where order_type= #{orderType} ") List<Order> getOrderBYId(@Param("orderType") Integer orderType); @Select(" SELECT o.id ,o.pay_no ,o.user_id ,o.product_id ,o.pay_price, o.num, o.pay_time, o.order_type, o.notif_type FROM order o where notify_type= #{notifyType} ") List<Order> getOrderBYId(@Param("notifyType") Integer notifyType);
需求c:對訂單表增加一個訂單狀態(tài)status,根據(jù)訂單狀態(tài)查詢訂單信息。
傳統(tǒng)mybaits需要三步:首先需要在訂單表里加個字段,然后在訂單的實體類添加這個屬性,并且將所有dao層設(shè)計該狀態(tài)的的查詢sql都修改一遍,加上這個字段。
1.1.3 上述方式有什么問題呢?
需求a:對于不同的實體類,即使查詢的目的一致,仍然需要重復(fù)構(gòu)造類似的sql語句,僅僅是表字段和表信息不同。
需求b:對于相似的查詢條件,針對某個單一場景必須構(gòu)造不同的sql,造成sql語句的大量冗余。
需求c:將dao層所有涉及到新增字段的sql都需要修改一遍,這個過程比較繁瑣且容易出錯。
使用mybatis-plus就可以解決上述問題。
1.1.4 Myatis-plus的解決方案
首先讓ProductMapper和OrderMapper繼承BaseMapper類:
public interface ProductMapper extends BaseMapper<Product> { } public interface OrderMapper extends BaseMapper<Order> { }
需求a:
分別根據(jù)id查詢商品表和訂單表:由于BaseMapper中提供了selectById的方法,可以直接根據(jù)具體業(yè)務(wù)場景,傳入指定的參數(shù)例如(id=1)即可。無需書寫具體的sql語句,至于sql自動生成的原理將在下面介紹;
productMapper.selectById(1); orderMapper.selectById(1);
需求b:
此時利用BaseMapper.selectList(Wapper queryWrapper)方法直接構(gòu)造查詢條件,例如查詢支付狀態(tài)為2和通知狀態(tài)為1的訂單信息
orderMapper.selectList(new QueryWrapper<Order>().eq("orderType",2)); orderMapper.selectList(new QueryWrapper<Order>().eq("notifyType",1)); orderMapper.selectList(new QueryWrapper<Order>().eq("orderType",2));orderMapper.selectList(new QueryWrapper<Order>().eq("notifyType",1));
此時我們可以發(fā)現(xiàn):使用了Mybatis-plus以后,我們更加聚焦于業(yè)務(wù)本身,對于上述相似的應(yīng)用場景,無需構(gòu)造雷同的SQL,利用包裝器直接傳入查詢條件。
需求c:
前兩步與傳統(tǒng)mybatis一致,由于MyBatis-plus無需手動創(chuàng)建SQL,因此減少了大量的重復(fù)勞動。
1.2 MyBatis-Plus的定位
MyBatis-Plus (opens new window)(簡稱 MP)是一個 MyBatis (opens new window)的增強(qiáng)工具,在 MyBatis 的基礎(chǔ)上只做增強(qiáng)不做改變,為簡化開發(fā)、提高效率而生。
1.3 特性
- 無侵入:只做增強(qiáng)不做改變,引入它不會對現(xiàn)有工程產(chǎn)生影響,如絲般順滑;
- 損耗小:啟動即會自動注入基本 CURD,性能基本無損耗,直接面向?qū)ο蟛僮鳎?/li>
- 強(qiáng)大的 CRUD 操作:內(nèi)置通用 Mapper、通用 Service,僅僅通過少量配置即可實現(xiàn)單表大部分 CRUD 操作,更有強(qiáng)大的條件構(gòu)造器,滿足各類使用需求;
- 支持 Lambda 形式調(diào)用:通過 Lambda 表達(dá)式,方便的編寫各類查詢條件,無需再擔(dān)心字段寫錯;
- 支持主鍵自動生成:支持多達(dá) 4 種主鍵策略(內(nèi)含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解決主鍵問題;
- 支持 ActiveRecord 模式:支持 ActiveRecord 形式調(diào)用,實體類只需繼承 Model 類即可進(jìn)行強(qiáng)大的 CRUD 操作;
- 支持自定義全局通用操作:支持全局通用方法注入( Write once, use anywhere );
- 內(nèi)置代碼生成器:采用代碼或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 層代碼,支持模板引擎,更有超多自定義配置等您來使用;
- 內(nèi)置分頁插件:基于 MyBatis 物理分頁,開發(fā)者無需關(guān)心具體操作,配置好插件之后,寫分頁等同于普通 List 查詢;
- 分頁插件支持多種數(shù)據(jù)庫:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多種數(shù)據(jù)庫;
- 內(nèi)置性能分析插件:可輸出 Sql 語句以及其執(zhí)行時間,建議開發(fā)測試時啟用該功能,能快速揪出慢查詢;
- 內(nèi)置全局?jǐn)r截插件:提供全表 delete 、 update 操作智能分析阻斷,也可自定義攔截規(guī)則,預(yù)防誤操作。
1.4 原理解析
本文針對MyBatis-plus中的核心功能:SQL自動注入功能,進(jìn)行流程分析及原理探究。
二、準(zhǔn)備工作
先從一個簡單的demo入手,感受一下MyBatis-plus的便捷性。
2.1 基礎(chǔ)接口BaseMapper
源碼中提供了一個基礎(chǔ)接口,里面包含了基本的增刪改查方法。
2.2 創(chuàng)建實體類對象
2.3 業(yè)務(wù)接口UserMapper
業(yè)務(wù)中根據(jù)具體實體對象,繼承該抽象接口。
2.4 測試用例
控制臺顯示:MyBatis-plus最終為我們自動生成了SQL語句。根據(jù)上述操作分析:UserMapper繼承了BaseMapper,擁有了deleteById的方法,但是MyBatis-plus是基于mybatis的增強(qiáng)版,關(guān)鍵在于最終仍然需要提供具體的SQL語句,來進(jìn)行數(shù)據(jù)庫操作。
下面就通過debug由上而下分析mybatis-plus是如何生成業(yè)務(wù)sql以及自動注入的。
三、SQL語句存儲在哪里?
mappedStatements:描述sql信息
如下圖所示:mybatis為我們生成了一個代理對象,里面包含了一些重要的屬性。
具體如下:
userMapper——>mybatisMapperProxy——>sqlSession——>sqlSessionFactory——>configuration——>mappedStatements——>mappedStatement——>sql語句
至此我們可以發(fā)現(xiàn)每一個SQL語句對應(yīng)一個mappedStatement,mappedstatements存儲在configuration文件(configuration是mybatis的全局配置文件,包含數(shù)據(jù)源、mapper、其他配置信息)中。
四、SQL語句是什么時候注入的?
4.1 AbstractMethod.addMappedStatement
基于上面的分析,想要知道SQL語句什么時候拿到的,就是要找到mappedStatement被添加的位置。追蹤到AbstractMethod的抽象方法中。
找到了addMappedStatement()方法
而BaseMapper的所有方法(deleteById、delete、insert、select、update等)都繼承了該抽象方法。
根據(jù)mapper方法(deleteById)顯然是調(diào)用addDeleteMappedStatement方法。
這里我們可以發(fā)現(xiàn),源碼中根據(jù)不同的方法繼承AbstractMethod實現(xiàn)了不同的實現(xiàn)類,并且實現(xiàn)了injectMappedStatement方法,sqlSource也是在這個地方被添加進(jìn)配置文件。
4.2 AbstractMethod.inject
繼續(xù)研究AbstractMethod抽象類,inject方法實現(xiàn)了自動注入sql的動作。
有上述源碼可知,項目啟動時,首先由默認(rèn)注入器生成基礎(chǔ)CRUD實現(xiàn)類對象,其次遍歷實現(xiàn)類列表,依次注入各自的模板SQL,最后將其添加至mappedstatement。
五、SQL語句是怎么生成的?
5.1 SQL模板
上述方法中有兩個關(guān)鍵的參數(shù):SqlMethod、SqlSource;
繼續(xù)研究源碼發(fā)現(xiàn):sqlMethod本質(zhì)上是一個枚舉類,存儲了兩個關(guān)鍵的元素:
BaseMapper中的方法名;
方法名對應(yīng)的sql語句模板(即被
到這里我們基本了解了mybaits-plus實現(xiàn)sql自動生成的本質(zhì):根據(jù)不同的方法來提供一些通用的模板,項目啟動后再加載進(jìn)mappedStatement。
5.2 SqlSource
此時SqlSource通過解析SQL模板、以及傳入的表信息和主鍵信息構(gòu)建出了SQL語句。
5.3 數(shù)據(jù)庫表信息是如何獲取的?
分析initTableName()方法:獲取表名信息源碼中傳入了實體類信息clazz,其實就是通過實體上的@TableName注解拿到了表名;
我們在定義實體類的同時,指定了該實體類對應(yīng)的表名。
分析initTableFields()方法:
獲取主鍵及其他字段信息
至此tableInfo的信息已經(jīng)注入完成了。
在研究完解析mapper的核心過程之后,我們再簡單看下mapper文件被添加到configuration(mybatis核心配置文件)的過程。
六、mapper文件被添加的過程
ISqlInjector:Sql注入器
MybatisMapperAnnotationBuilder:mapper解析器
MybatisMapperAnnotationBuilder中的parse方法獲取了sqlInjector(Sql注入器)來進(jìn)行SQL注入。
Mybatis添加mapper的固有流程:MybatisMapperRegistry
調(diào)用MapperAnnotionBuilder解析器進(jìn)行解析
MybatisConfiguration.addMapper
MybatisXMLConfigBuilder.mapperElemnt
MybayisXMLConfigBuilder.parseConfiguration
添加mapper文件的過程分析到這里就完成了。
七、總結(jié)
7.1 流程梳理
下面總結(jié)梳理了一下mybatis-plus解析mapper文件自動注入sql的主要流程。
7.2 Mybatis-plus的ORM的核心思想
1)實體類和數(shù)據(jù)庫表通過自定義注解來完成一一映射。
2)對象屬性和字段同樣使用注解來一一對應(yīng)(命名注意要相同)。
3)為了提高復(fù)用性使得具體的mapper繼承通用的增刪改查接口。
4)利用模板方法和對象屬性值動態(tài)拼接SQL。
八、參考文檔
MyBatis-plus官方文檔:https://mp.baomidou.com/
作者:vivo互聯(lián)網(wǎng)服務(wù)器團(tuán)隊-Li Lei
到此這篇關(guān)于Mybatis-Plus的應(yīng)用場景及注入SQL原理分析的文章就介紹到這了,更多相關(guān)Mybatis-Plus sql注入內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
濫用@PathVariable導(dǎo)致bug原因分析解決
這篇文章主要為大家介紹了濫用@PathVariable導(dǎo)致bug原因分析解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12詳解JAVAEE——SSH三大框架整合(spring+struts2+hibernate)
這篇文章主要介紹了詳解JAVAEE——SSH三大框架整合(spring+struts2+hibernate),具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-07-07Spring?MVC DispatcherServlet處理請求過程示例詳解
這篇文章主要介紹了Spring?MVC?DispatcherServlet處理請求過程示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09