1. 面試官問:Mybatis Plus 是如何實現(xiàn)動態(tài) SQL 語句的?原理你懂嗎?

        共 6180字,需瀏覽 13分鐘

         ·

        2021-11-27 19:50


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


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



        # 入口類: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個功能類


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

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


        # MybatisConfiguration類


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


        MybatisConfiguration中很多方法是使用MybatisMapperRegistry進行重寫實現(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方法中,真正進入到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注入默認的方法列表**/????????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)進入mybatis自有功能。


        # 總結(jié)一下


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



        總體上來說,MP實現(xiàn)mybatis的增強,手段略顯繁瑣和不夠直觀,其實根據(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)過程,并且給出一個可能的更便捷方法。


        來源:juejin.cn/post/6883081187103866894



        ——————END——————

        歡迎關(guān)注“Java引導(dǎo)者”,我們分享最有價值的Java的干貨文章,助力您成為有思想的Java開發(fā)工程師!

        瀏覽 70
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

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

        手機掃一掃分享

        分享
        舉報
          
          

            1. 国产成人精品一区二区三区四区 | 搓光美女衣服羞羞漫画入口 | 色噜噜狠狠成人中文 | 他揉捏她两乳不停呻吟a视频 | 久久午夜电影院 |