<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          面試官問:Mybatis Plus 是如何實(shí)現(xiàn)動態(tài) SQL 語句的?

          共 6294字,需瀏覽 13分鐘

           ·

          2022-01-20 09:00

          不點(diǎn)藍(lán)字關(guān)注,我們哪來故事?




          ?正文如下?

          來源:juejin.cn/post/6883081187103866894


          Mybatis-Plus(簡稱MP)是一個 Mybatis 的增強(qiáng)工具,那么它是怎么增強(qiáng)的呢?其實(shí)就是它已經(jīng)封裝好了一些crud方法,開發(fā)就不需要再寫xml了,直接調(diào)用這些方法就行,就類似于JPA。


          那么這篇文章就來閱讀以下MP的具體實(shí)現(xiàn),看看是怎樣實(shí)現(xiàn)這些增強(qiáng)的。



          # 入口類:MybatisSqlSessionFactoryBuilder


          通過在入口類 MybatisSqlSessionFactoryBuilder#build方法中, 在應(yīng)用啟動時(shí), 將mybatis plus(簡稱MP)自定義的動態(tài)配置xml文件注入到Mybatis中。


          public?class?MybatisSqlSessionFactoryBuilder?extends?SqlSessionFactoryBuilder?{????public?SqlSessionFactory?build(Configuration?configuration)?{????????????//?...?省略若干行????????????if?(globalConfig.isEnableSqlRunner())?{????????????????new?SqlRunnerInjector().inject(configuration);????????????}????????????//?...?省略若干行????????????return?sqlSessionFactory;????????}}

          這里涉及到2個MP2個功能類


          • 擴(kuò)展繼承自Mybatis的MybatisConfiguration類: MP動態(tài)腳本構(gòu)建,注冊,及其它邏輯判斷。

          • SqlRunnerInjector: MP默認(rèn)插入一些動態(tài)方法的xml 腳本方法。


          # MybatisConfiguration類


          這里我們重點(diǎn)剖析MybatisConfiguration類,在MybatisConfiguration中,MP初始化了其自身的MybatisMapperRegistry,而MybatisMapperRegistry是MP加載自定義的SQL方法的注冊器。


          MybatisConfiguration中很多方法是使用MybatisMapperRegistry進(jìn)行重寫實(shí)現(xiàn)。


          其中有3個重載方法addMapper實(shí)現(xiàn)了注冊MP動態(tài)腳本的功能。


          public?class?MybatisConfiguration?extends?Configuration?{????/**?????*?Mapper?注冊?????*/????protected?final?MybatisMapperRegistry?mybatisMapperRegistry?=?new?MybatisMapperRegistry(this);    // ....
          ????/**?????*?初始化調(diào)用?????*/????public?MybatisConfiguration()?{????????super();????????this.mapUnderscoreToCamelCase?=?true;????????languageRegistry.setDefaultDriverClass(MybatisXMLLanguageDriver.class); }????/**?????* MybatisPlus 加載 SQL 順序:?????*?

          ?1、加載?XML中的?SQL?

          ?????*?

          ?2、加載?SqlProvider?中的?SQL?

          ?????*?

          ?3、XmlSql?與?SqlProvider不能包含相同的?SQL?

          ?????*?

          調(diào)整后的 SQL優(yōu)先級:XmlSql > sqlProvider > CurdSql

          ?????*/????@Override????public?void?addMappedStatement(MappedStatement?ms)?{????????//?... }????//?...?省略若干行????/**?????*?使用自己的?MybatisMapperRegistry?????*/????@Override????public??void?addMapper(Class?type)?{????????mybatisMapperRegistry.addMapper(type);????}????//?....?省略若干行}

          在MybatisMapperRegistry中,MP將mybatis的MapperAnnotationBuilder替換為MP自己的MybatisMapperAnnotationBuilder


          public?class?MybatisMapperRegistry?extends?MapperRegistry?{????@Override????public??void?addMapper(Class?type)?{????????//?...?省略若干行????????MybatisMapperAnnotationBuilder?parser?=?new?MybatisMapperAnnotationBuilder(config,?type);????????parser.parse();????????//?...?省略若干行????}}

          在MybatisMapperRegistry類的addMapper方法中,真正進(jìn)入到MP的核心類MybatisMapperAnnotationBuilder,MybatisMapperAnnotationBuilder這個類是MP實(shí)現(xiàn)動態(tài)腳本的關(guān)鍵類。


          # MybatisMapperAnnotationBuilder動態(tài)構(gòu)造


          在MP的核心類MybatisMapperAnnotationBuilder的parser方法中,MP逐一遍歷要加載的Mapper類,加載的方法包括下面幾個


          public?class?MybatisMapperAnnotationBuilder?extends?MapperAnnotationBuilder?{????@Overrde????public?void?parse()?{????????//...?省略若干行????????for?(Method?method?:?type.getMethods())?{????????????/**?for循環(huán)代碼,?MP判斷method方法是否是@Select?@Insert等mybatis注解方法**/????????????parseStatement(method);????????????InterceptorIgnoreHelper.initSqlParserInfoCache(cache,?mapperName,?method);????????????SqlParserHelper.initSqlParserInfoCache(mapperName,?method);????????}????????/**?這2行代碼,?MP注入默認(rèn)的方法列表**/????????if?(GlobalConfigUtils.isSupperMapperChildren(configuration,?type))?{????????????GlobalConfigUtils.getSqlInjector(configuration).inspectInject(assistant,?type);????????}????????//...?省略若干行    }
          ????@Override????public?void?inspectInject(MapperBuilderAssistant?builderAssistant,?Class?mapperClass)?{????????Class?modelClass?=?extractModelClass(mapperClass);????????//...?省略若干行????????List?methodList?=?this.getMethodList(mapperClass);????????TableInfo?tableInfo?=?TableInfoHelper.initTableInfo(builderAssistant,?modelClass);????????//?循環(huán)注入自定義方法????????methodList.forEach(m?->?m.inject(builderAssistant,?mapperClass,?modelClass,?tableInfo));????????mapperRegistryCache.add(className);????}}public class DefaultSqlInjector extends AbstractSqlInjector {
          ????@Override????public?List?getMethodList(Class?mapperClass)?{????????return?Stream.of(????????????new?Insert(),????????????//...?省略若干行????????????new?SelectPage()????????).collect(toList());????}}


          在MybatisMapperAnnotationBuilder中,MP真正將框架自定義的動態(tài)SQL語句注冊到Mybatis引擎中。而AbstractMethod則履行了具體方法的SQL語句構(gòu)造。


          # 具體的AbstractMethod實(shí)例類,構(gòu)造具體的方法SQL語句


          以 SelectById 這個類為例說明下


          /**?*?根據(jù)ID?查詢一條數(shù)據(jù)?*/public?class?SelectById?extends?AbstractMethod?{????@Override????public?MappedStatement?injectMappedStatement(Class?mapperClass,?Class?modelClass,?TableInfo?tableInfo)?{????????/**?定義?mybatis?xml?method?id,?對應(yīng)??**/        SqlMethod sqlMethod = SqlMethod.SELECT_BY_ID;????????/**?構(gòu)造id對應(yīng)的具體xml片段?**/????????SqlSource?sqlSource?=?new?RawSqlSource(configuration,?String.format(sqlMethod.getSql(),????????????sqlSelectColumns(tableInfo,?false),????????????tableInfo.getTableName(),?tableInfo.getKeyColumn(),?tableInfo.getKeyProperty(),????????????tableInfo.getLogicDeleteSql(true,?true)),?Object.class);????????/**?將xml?method方法添加到mybatis的MappedStatement中?**/????????return?this.addSelectMappedStatementForTable(mapperClass,?getMethod(sqlMethod),?sqlSource,?tableInfo);????}}

          至此,MP完成了在啟動時(shí)加載自定義的方法xml配置的過程,后面的就是mybatis ${變量} #{變量}的動態(tài)替換和預(yù)編譯,已經(jīng)進(jìn)入mybatis自有功能。


          # 總結(jié)一下


          MP總共改寫和替換了mybatis的十多個類,主要如下圖所示:



          總體上來說,MP實(shí)現(xiàn)mybatis的增強(qiáng),手段略顯繁瑣和不夠直觀,其實(shí)根據(jù)MybatisMapperAnnotationBuilder構(gòu)造出自定義方法的xml文件,將其轉(zhuǎn)換為mybatis的Resource資源,可以只繼承重寫一個Mybatis類:SqlSessionFactoryBean 比如如下:


          public class YourSqlSessionFactoryBean extends SqlSessionFactoryBean implements ApplicationContextAware {
          ????private?Resource[]?mapperLocations;????????@Override????public?void?setMapperLocations(Resource...?mapperLocations)?{????????super.setMapperLocations(mapperLocations);????????/**?存使用mybatis原生定義的mapper?xml文件路徑**/????????this.mapperLocations?=?mapperLocations; }
          ????/**?????*?{@inheritDoc}?????*/????@Override????public?void?afterPropertiesSet()?throws?Exception?{????????ConfigurableListableBeanFactory?beanFactory?=?getBeanFactory();????????/**?只需要通過將自定義的方法構(gòu)造成xml?resource和原生定義的Resource一起注入到mybatis中即可,?這樣就可以實(shí)現(xiàn)MP的自定義動態(tài)SQL和原生SQL的共生關(guān)系**/????????this.setMapperLocations(InjectMapper.getMapperResource(this.dbType,?beanFactory,?this.mapperLocations));????????super.afterPropertiesSet();????}}

          在這篇文章中,簡單介紹了MP實(shí)現(xiàn)動態(tài)語句的實(shí)現(xiàn)過程,并且給出一個可能的更便捷方法。


          往期推薦

          Mybatis-Plus 官方神器 mate 發(fā)布!

          為什么 Netflix 能做到“永不宕機(jī)”?

          不同階段的員工離職,考慮什么?

          Java 調(diào)用第三方接口示范

          SpringBoot 整合 ClickHouse 教程


          //////?END?//////
          ↓ 點(diǎn)擊下方關(guān)注,看更多架構(gòu)分享?↓

          ↓ 或加泥瓦匠微信,交流更多技術(shù)?↓

          瀏覽 24
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  老黄色网址 | 黄色电影免费观看a | 黄色视频在线免费观看高清视频 | 91左爱在线 | 亚洲天堂1|