假設有兩張表:一張商品表、一張訂單表,具體表的字段以下:java
現有以下需求:sql
需求a:根據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);
根據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:根據支付狀態和通知狀態查詢訂單表信息mybatis
@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:對訂單表增長一個訂單狀態status,根據訂單狀態查詢訂單信息。app
傳統mybaits須要三步:首先須要在訂單表裏加個字段,而後在訂單的實體類添加這個屬性,而且將全部dao層設計該狀態的的查詢sql都修改一遍,加上這個字段。分佈式
需求a:對於不一樣的實體類,即便查詢的目的一致,仍然須要重複構造相似的sql語句,僅僅是表字段和表信息不一樣。工具
需求b:對於類似的查詢條件,針對某個單一場景必須構造不一樣的sql,形成sql語句的大量冗餘。性能
需求c:將dao層全部涉及到新增字段的sql都須要修改一遍,這個過程比較繁瑣且容易出錯。測試
使用mybatis-plus就能夠解決上述問題。
首先讓ProductMapper和OrderMapper繼承BaseMapper類:
public interface ProductMapper extends BaseMapper<Product> { } public interface OrderMapper extends BaseMapper<Order> { }
需求a:
分別根據id查詢商品表和訂單表:因爲BaseMapper中提供了selectById的方法,能夠直接根據具體業務場景,傳入指定的參數例如(id=1)便可。無需書寫具體的sql語句,至於sql自動生成的原理將在下面介紹;
productMapper.selectById(1); orderMapper.selectById(1);
需求b:
此時利用BaseMapper.selectList(Wapper queryWrapper)方法直接構造查詢條件,例如查詢支付狀態爲2和通知狀態爲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));
此時咱們能夠發現:使用了Mybatis-plus之後,咱們更加聚焦於業務自己,對於上述類似的應用場景,無需構造雷同的SQL,利用包裝器直接傳入查詢條件。
需求c:
前兩步與傳統mybatis一致,因爲MyBatis-plus無需手動建立SQL,所以減小了大量的重複勞動。
MyBatis-Plus (opens new window)(簡稱 MP)是一個 MyBatis (opens new window)的加強工具,在 MyBatis 的基礎上只作加強不作改變,爲簡化開發、提升效率而生。
本文針對MyBatis-plus中的核心功能:SQL自動注入功能,進行流程分析及原理探究。
先從一個簡單的demo入手,感覺一下MyBatis-plus的便捷性。
源碼中提供了一個基礎接口,裏面包含了基本的增刪改查方法。
業務中根據具體實體對象,繼承該抽象接口。
控制檯顯示:MyBatis-plus最終爲咱們自動生成了SQL語句。根據上述操做分析:UserMapper繼承了BaseMapper,擁有了deleteById的方法,可是MyBatis-plus是基於mybatis的加強版,關鍵在於最終仍然須要提供具體的SQL語句,來進行數據庫操做。
下面就經過debug由上而下分析mybatis-plus是如何生成業務sql以及自動注入的。
mappedStatements:描述sql信息
以下圖所示:mybatis爲咱們生成了一個代理對象,裏面包含了一些重要的屬性。
具體以下:
userMapper——>mybatisMapperProxy——>sqlSession——>sqlSessionFactory——>configuration——>mappedStatements——>mappedStatement——>sql語句
至此咱們能夠發現每個SQL語句對應一個mappedStatement,mappedstatements存儲在configuration文件(configuration是mybatis的全局配置文件,包含數據源、mapper、其餘配置信息)中。
基於上面的分析,想要知道SQL語句何時拿到的,就是要找到mappedStatement被添加的位置。追蹤到AbstractMethod的抽象方法中。
找到了addMappedStatement()方法
而BaseMapper的全部方法(deleteById、delete、insert、select、update等)都繼承了該抽象方法。
根據mapper方法(deleteById)顯然是調用addDeleteMappedStatement方法。
這裏咱們能夠發現,源碼中根據不一樣的方法繼承AbstractMethod實現了不一樣的實現類,而且實現了injectMappedStatement方法,sqlSource也是在這個地方被添加進配置文件。
繼續研究AbstractMethod抽象類,inject方法實現了自動注入sql的動做。
有上述源碼可知,項目啓動時,首先由默認注入器生成基礎CRUD實現類對象,其次遍歷實現類列表,依次注入各自的模板SQL,最後將其添加至mappedstatement。
上述方法中有兩個關鍵的參數:SqlMethod、SqlSource;
繼續研究源碼發現:sqlMethod本質上是一個枚舉類,存儲了兩個關鍵的元素:
BaseMapper中的方法名;
方法名對應的sql語句模板(即被<scripe>標籤包裹的字符串)。
到這裏咱們基本瞭解了mybaits-plus實現sql自動生成的本質:根據不一樣的方法來提供一些通用的模板,項目啓動後再加載進mappedStatement。
此時SqlSource經過解析SQL模板、以及傳入的表信息和主鍵信息構建出了SQL語句。
分析initTableName()方法:獲取表名信息源碼中傳入了實體類信息clazz,其實就是經過實體上的@TableName註解拿到了表名;
咱們在定義實體類的同時,指定了該實體類對應的表名。
分析initTableFields()方法:
獲取主鍵及其餘字段信息
至此tableInfo的信息已經注入完成了。
在研究完解析mapper的核心過程以後,咱們再簡單看下mapper文件被添加到configuration(mybatis核心配置文件)的過程。
ISqlInjector:Sql注入器
MybatisMapperAnnotationBuilder:mapper解析器
MybatisMapperAnnotationBuilder中的parse方法獲取了sqlInjector(Sql注入器)來進行SQL注入。
Mybatis添加mapper的固有流程:MybatisMapperRegistry
調用MapperAnnotionBuilder解析器進行解析
MybatisConfiguration.addMapper
MybatisXMLConfigBuilder.mapperElemnt
MybayisXMLConfigBuilder.parseConfiguration
添加mapper文件的過程分析到這裏就完成了。
下面總結梳理了一下mybatis-plus解析mapper文件自動注入sql的主要流程。
1)實體類和數據庫表經過自定義註解來完成一一映射。
2)對象屬性和字段一樣使用註解來一一對應(命名注意要相同)。
3)爲了提升複用性使得具體的mapper繼承通用的增刪改查接口。
4)利用模板方法和對象屬性值動態拼接SQL。
MyBatis-plus官方文檔:https://mp.baomidou.com/
做者:vivo互聯網服務器團隊-Li Lei