<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)動(dòng)態(tài) SQL 語句的?原理你懂嗎?

          共 11811字,需瀏覽 24分鐘

           ·

          2021-12-09 11:09

          作者 | 稻草江南

          來源 | https://juejin.cn/post/6883081187103866894

          Mybatis-Plus(簡(jiǎn)稱MP)是一個(gè) 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)用啟動(dòng)時(shí), 將mybatis plus(簡(jiǎn)稱MP)自定義的動(dòng)態(tài)配置xml文件注入到Mybatis中。

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

          這里涉及到2個(gè)MP2個(gè)功能類

          • 擴(kuò)展繼承自Mybatis的MybatisConfiguration類: MP動(dòng)態(tài)腳本構(gòu)建,注冊(cè),及其它邏輯判斷。
          • SqlRunnerInjector: MP默認(rèn)插入一些動(dòng)態(tài)方法的xml 腳本方法。

          如果您正在學(xué)習(xí)Spring Boot,那么推薦一個(gè)連載多年還在繼續(xù)更新的免費(fèi)教程:http://blog.didispace.com/spring-boot-learning-2x/

          MybatisConfiguration類

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

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

          其中有3個(gè)重載方法addMapper實(shí)現(xiàn)了注冊(cè)MP動(dòng)態(tài)腳本的功能。推薦一個(gè) Spring Boot 基礎(chǔ)教程及實(shí)戰(zhàn)示例:https://github.com/javastacks/spring-boot-best-practice

          public class MybatisConfiguration extends Configuration {
              /**
               * Mapper 注冊(cè)
               */

              protected final MybatisMapperRegistry mybatisMapperRegistry = new MybatisMapperRegistry(this);
              // ....

              /**
               * 初始化調(diào)用
               */

              public MybatisConfiguration() {
                  super();
                  this.mapUnderscoreToCamelCase = true;
                  languageRegistry.setDefaultDriverClass(MybatisXMLLanguageDriver.class);
              }

              /**
               * MybatisPlus 加載 SQL 順序:
               * <p> 1、加載 XML中的 SQL </p>
               * <p> 2、加載 SqlProvider 中的 SQL </p>
               * <p> 3、XmlSql 與 SqlProvider不能包含相同的 SQL </p>
               * <p>調(diào)整后的 SQL優(yōu)先級(jí):XmlSql > sqlProvider > CurdSql </p>
               */

              @Override
              public void addMappedStatement(MappedStatement ms) {
                  // ...
              }

              // ... 省略若干行
              /**
               * 使用自己的 MybatisMapperRegistry
               */

              @Override
              public <T> void addMapper(Class<T> type) {
                  mybatisMapperRegistry.addMapper(type);
              }
              // .... 省略若干行
          }

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

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

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

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

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

          public class MybatisMapperAnnotationBuilder extends MapperAnnotationBuilder {
              @Override
              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<AbstractMethod> 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<AbstractMethod> getMethodList(Class<?> mapperClass) {
                  return Stream.of(
                      new Insert(),
                      //... 省略若干行
                      new SelectPage()
                  ).collect(toList());
              }
          }

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

          如果您正在學(xué)習(xí)Spring Cloud,推薦一個(gè)經(jīng)典教程(含Spring Cloud Alibaba):https://blog.didispace.com/spring-cloud-learning/

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

          以 SelectById 這個(gè)類為例說明下

          /**
           * 根據(jù)ID 查詢一條數(shù)據(jù)
           */

          public class SelectById extends AbstractMethod {
              @Override
              public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
                  /** 定義 mybatis xml method id, 對(duì)應(yīng) <id="xyz"> **/
                  SqlMethod sqlMethod = SqlMethod.SELECT_BY_ID;
                  /** 構(gòu)造id對(duì)應(yīng)的具體xml片段 **/
                  SqlSource sqlSource = new RawSqlSource(configuration, String.format(sqlMethod.getSql(),
                      sqlSelectColumns(tableInfo, false),
                      tableInfo.getTableName(), tableInfo.getKeyColumn(), tableInfo.getKeyProperty(),
                      tableInfo.getLogicDeleteSql(truetrue)), Object.class);
                  /** 將xml method方法添加到mybatis的MappedStatement中 **/
                  return this.addSelectMappedStatementForTable(mapperClass, getMethod(sqlMethod), sqlSource, tableInfo);
              }
          }

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

          總結(jié)一下

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

          總體上來說,MP實(shí)現(xiàn)mybatis的增強(qiáng),手段略顯繁瑣和不夠直觀,其實(shí)根據(jù)MybatisMapperAnnotationBuilder構(gòu)造出自定義方法的xml文件,將其轉(zhuǎn)換為mybatis的Resource資源,可以只繼承重寫一個(gè)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的自定義動(dòng)態(tài)SQL和原生SQL的共生關(guān)系**/
                  this.setMapperLocations(InjectMapper.getMapperResource(this.dbType, beanFactory, this.mapperLocations));
                  super.afterPropertiesSet();
              }
          }

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

          往期推薦



          IDEA 2021.3 正式發(fā)布:支持遠(yuǎn)程開發(fā)、故障排查、Java和Spring等多項(xiàng)優(yōu)化改進(jìn)

          如何防止你的 jar 被反編譯?

          Jetbrains推出新一代編輯器:Fleet,網(wǎng)友:VS Code迎來勁敵JB Code!

          免費(fèi)開源、功能完善、暗黑風(fēng)格,你會(huì)拒絕這款SSH工具嗎?

          那個(gè)當(dāng)上非洲酋長的交大才子,如今怎么樣了?



          技術(shù)交流群

          最近有很多人問,有沒有讀者交流群,想知道怎么加入。加入方式很簡(jiǎn)單,有興趣的同學(xué),只需要點(diǎn)擊下方卡片,回復(fù)“加群,即可免費(fèi)加入我們的高質(zhì)量技術(shù)交流群!

          點(diǎn)擊閱讀原文,送你免費(fèi)Spring Boot教程!

          瀏覽 54
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

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

          手機(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>
                  一二三乱伦| 成人亚洲精品一区二区三区嫩花 | 亚洲人成色777777无码 | 免费的日韩欧美黄色片 | 亚洲大胆视频 |