<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 是如何實現(xiàn)動態(tài) SQL 語句的?原理你懂嗎?

          共 6351字,需瀏覽 13分鐘

           ·

          2021-12-01 01:19

          程序員的成長之路
          互聯(lián)網(wǎng)/程序員/技術(shù)/資料共享?
          關(guān)注


          閱讀本文大概需要 4?分鐘。

          來源:juejin.cn/post/6883081187103866894


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

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


          # 入口類:MybatisSqlSessionFactoryBuilder


          通過在入口類 MybatisSqlSessionFactoryBuilder#build方法中, 在應(yīng)用啟動時, 將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類


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

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

          其中有3個重載方法addMapper實現(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實現(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實例類,構(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完成了在啟動時加載自定義的方法xml配置的過程,后面的就是mybatis ${變量} #{變量}的動態(tài)替換和預(yù)編譯,已經(jīng)進(jìn)入mybatis自有功能。

          # 總結(jié)一下


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



          總體上來說,MP實現(xiàn)mybatis的增強(qiáng),手段略顯繁瑣和不夠直觀,其實根據(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中即可,?這樣就可以實現(xiàn)MP的自定義動態(tài)SQL和原生SQL的共生關(guān)系**/????????this.setMapperLocations(InjectMapper.getMapperResource(this.dbType,?beanFactory,?this.mapperLocations));????????super.afterPropertiesSet();????}}

          在這篇文章中,簡單介紹了MP實現(xiàn)動態(tài)語句的實現(xiàn)過程,并且給出一個可能的更便捷方法。
          推薦閱讀:
          妙用Java 8中的 Function接口 消滅if...else(非常新穎的寫法)
          大文件上傳服務(wù)器、支持超大文件HTTP斷點續(xù)傳實踐總結(jié)

          最近面試BAT,整理一份面試資料《Java面試BATJ通關(guān)手冊》,覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務(wù)、數(shù)據(jù)庫、數(shù)據(jù)結(jié)構(gòu)等等。

          獲取方式:點個「在看」,點擊上方小卡片,進(jìn)入公眾號后回復(fù)「面試題」領(lǐng)取,更多內(nèi)容陸續(xù)奉上。

          朕已閱?

          瀏覽 68
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <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>
                  欧美成人免费看片一区 | 一道夲一二三区区 | 天天爱天天干天天 | 黑妞操逼 | 日日噜狠狠色综合久 |