mybatis-plus - buildSqlSessionFactory()


一. buildSqlSessionFactory()

mybatis-plus 同樣的是調用  factory.getObject() 方法來進行 SqlSessionFactory 創建的. 然后調用 buildSqlSessionFactory() 方法:

    protected SqlSessionFactory buildSqlSessionFactory() throws Exception {

        MybatisConfiguration configuration;

        // TODO 加載自定義 MybatisXmlConfigBuilder
        MybatisXMLConfigBuilder xmlConfigBuilder = null;
        if (this.configuration != null) {
            configuration = this.configuration;
            if (configuration.getVariables() == null) {
                configuration.setVariables(this.configurationProperties);
            } else if (this.configurationProperties != null) {
                configuration.getVariables().putAll(this.configurationProperties);
            }
        } else if (this.configLocation != null) {
            xmlConfigBuilder = new MybatisXMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
            configuration = xmlConfigBuilder.getConfiguration();
        } else {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
            }
            // TODO 使用自定義配置
            configuration = new MybatisConfiguration();
            if (this.configurationProperties != null) {
                configuration.setVariables(this.configurationProperties);
            }
        }

        if (this.globalConfig == null) {
            this.globalConfig = GlobalConfigUtils.defaults();
        }
        if (this.globalConfig.getDbConfig() == null) {
            this.globalConfig.setDbConfig(new GlobalConfig.DbConfig());
        }

        // TODO 初始化 id-work 以及 打印騷東西
        configuration.init(this.globalConfig);

        if (this.objectFactory != null) {
            configuration.setObjectFactory(this.objectFactory);
        }

        if (this.objectWrapperFactory != null) {
            configuration.setObjectWrapperFactory(this.objectWrapperFactory);
        }

        if (this.vfs != null) {
            configuration.setVfsImpl(this.vfs);
        }

        if (hasLength(this.typeAliasesPackage)) {
            // TODO 支持自定義通配符
            List<String> typeAliasPackageList = new ArrayList<>();
            if (typeAliasesPackage.contains(StringPool.ASTERISK) && !typeAliasesPackage.contains(StringPool.COMMA) && !typeAliasesPackage.contains(StringPool.SEMICOLON)) {
                String[] convertTypeAliasesPackages = PackageHelper.convertTypeAliasesPackage(this.typeAliasesPackage);
                if (ArrayUtils.isEmpty(convertTypeAliasesPackages)) {
                    LOGGER.warn("Can't find class in '[" + this.typeAliasesPackage + "]' package. Please check your configuration.");
                } else {
                    typeAliasPackageList.addAll(Arrays.asList(convertTypeAliasesPackages));
                }
            } else {
                String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
                for (String one : typeAliasPackageArray) {
                    if (one.contains(StringPool.ASTERISK)) {
                        String[] convertTypeAliasesPackages = PackageHelper.convertTypeAliasesPackage(one);
                        if (ArrayUtils.isEmpty(convertTypeAliasesPackages)) {
                            LOGGER.warn("Can't find class in '[" + one + "]' package. Please check your configuration.");
                        } else {
                            typeAliasPackageList.addAll(Arrays.asList(convertTypeAliasesPackages));
                        }
                    } else {
                        typeAliasPackageList.add(one);
                    }
                }
            }
            for (String packageToScan : typeAliasPackageList) {
                configuration.getTypeAliasRegistry().registerAliases(packageToScan,
                    typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Scanned package: '" + packageToScan + "' for aliases");
                }
            }
        }

        // TODO 自定義枚舉類掃描處理
        if (hasLength(this.typeEnumsPackage)) {
            Set<Class> classes;
            if (typeEnumsPackage.contains(StringPool.STAR) && !typeEnumsPackage.contains(StringPool.COMMA)
                && !typeEnumsPackage.contains(StringPool.SEMICOLON)) {
                classes = PackageHelper.scanTypePackage(typeEnumsPackage);
                if (classes.isEmpty()) {
                    LOGGER.warn("Can't find class in '[" + typeEnumsPackage + "]' package. Please check your configuration.");
                }
            } else {
                String[] typeEnumsPackageArray = tokenizeToStringArray(this.typeEnumsPackage,
                    ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
                Assert.notNull(typeEnumsPackageArray, "not find typeEnumsPackage:" + typeEnumsPackage);
                classes = new HashSet<>();
                for (String typePackage : typeEnumsPackageArray) {
                    Set<Class> scanTypePackage = PackageHelper.scanTypePackage(typePackage);
                    if (scanTypePackage.isEmpty()) {
                        LOGGER.warn("Can't find class in '[" + typePackage + "]' package. Please check your configuration.");
                    } else {
                        classes.addAll(PackageHelper.scanTypePackage(typePackage));
                    }
                }
            }
            // 取得類型轉換注冊器
            TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
            for (Class cls : classes) {
                if (cls.isEnum()) {
                    if (IEnum.class.isAssignableFrom(cls)) {
                        // 接口方式
                        typeHandlerRegistry.register(cls, EnumTypeHandler.class);
                    } else {
                        // 注解方式
                        Class<?> clazz = dealEnumType(cls);
                        if (null != clazz) {
                            typeHandlerRegistry.register(cls, EnumAnnotationTypeHandler.class);
                        } else {
                            // 原生方式
                            registerOriginalEnumTypeHandler(typeHandlerRegistry, cls);
                        }
                    }
                }
            }
        }

        if (!isEmpty(this.typeAliases)) {
            for (Class<?> typeAlias : this.typeAliases) {
                configuration.getTypeAliasRegistry().registerAlias(typeAlias);
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Registered type alias: '" + typeAlias + "'");
                }
            }
        }

        if (!isEmpty(this.plugins)) {
            for (Interceptor plugin : this.plugins) {
                configuration.addInterceptor(plugin);
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Registered plugin: '" + plugin + "'");
                }
            }
        }

        if (hasLength(this.typeHandlersPackage)) {
            String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage,
                ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
            for (String packageToScan : typeHandlersPackageArray) {
                configuration.getTypeHandlerRegistry().register(packageToScan);
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Scanned package: '" + packageToScan + "' for type handlers");
                }
            }
        }

        if (!isEmpty(this.typeHandlers)) {
            for (TypeHandler<?> typeHandler : this.typeHandlers) {
                configuration.getTypeHandlerRegistry().register(typeHandler);
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Registered type handler: '" + typeHandler + "'");
                }
            }
        }

        if (this.databaseIdProvider != null) {//fix #64 set databaseId before parse mapper xmls
            try {
                configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
            } catch (SQLException e) {
                throw new NestedIOException("Failed getting a databaseId", e);
            }
        }

        if (this.cache != null) {
            configuration.addCache(this.cache);
        }

        if (xmlConfigBuilder != null) {
            try {
                xmlConfigBuilder.parse();

                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Parsed configuration file: '" + this.configLocation + "'");
                }
            } catch (Exception ex) {
                throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
            } finally {
                ErrorContext.instance().reset();
            }
        }

        if (this.transactionFactory == null) {
            this.transactionFactory = new SpringManagedTransactionFactory();
        }

        configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource));

        // TODO 設置元數據相關 如果用戶沒有配置 dbType 則自動獲取
        if (globalConfig.getDbConfig().getDbType() == DbType.OTHER) {
            try (Connection connection = AopUtils.getTargetObject(this.dataSource).getConnection()) {
                globalConfig.getDbConfig().setDbType(JdbcUtils.getDbType(connection.getMetaData().getURL()));
            } catch (Exception e) {
                throw ExceptionUtils.mpe("Error: GlobalConfigUtils setMetaData Fail !  Cause:" + e);
            }
        }
        SqlSessionFactory sqlSessionFactory = this.sqlSessionFactoryBuilder.build(configuration);

        // TODO SqlRunner
        SqlHelper.FACTORY = sqlSessionFactory;

        // TODO 設置全局參數屬性 以及 緩存 sqlSessionFactory
        globalConfig.signGlobalConfig(sqlSessionFactory);

        if (!isEmpty(this.mapperLocations)) {
            if (globalConfig.isRefresh()) {
                //TODO 設置自動刷新配置 減少配置
                new MybatisMapperRefresh(this.mapperLocations, sqlSessionFactory, 2,
                    2, true);
            }
            for (Resource mapperLocation : this.mapperLocations) {
                if (mapperLocation == null) {
                    continue;
                }

                try {
                    // TODO  這里也換了噢噢噢噢
                    XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
                        configuration, mapperLocation.toString(), configuration.getSqlFragments());
                    xmlMapperBuilder.parse();
                } catch (Exception e) {
                    throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
                } finally {
                    ErrorContext.instance().reset();
                }

                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Parsed mapper file: '" + mapperLocation + "'");
                }
            }
        } else {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found");
            }
        }
        return sqlSessionFactory;
    }

方法和mybatis中的一樣, 非常的長.

this.sqlSessionFactoryBuilder.build(configuration) 這里返回的是 : return new DefaultSqlSessionFactory(config);

 

這里先只看  xmlMapperBuilder.parse() 方法, 因為這是主流程的方法, 同時, 他也是 mybatis 中的方法: 

  public void parse() {
    if (!configuration.isResourceLoaded(resource)) {
      configurationElement(parser.evalNode("/mapper"));
      configuration.addLoadedResource(resource);
      bindMapperForNamespace();
    }

    parsePendingResultMaps();
    parsePendingCacheRefs();
    parsePendingStatements();
  }
configurationElement 是解析 mapper.xml 文件. 很明顯, 這里拿不到通用mapper的那些方法對應的 sql 信息. 
接着來
bindMapperForNamespace() 方法:
  private void bindMapperForNamespace() {
    String namespace = builderAssistant.getCurrentNamespace();
    if (namespace != null) {
      Class<?> boundType = null;
      try {
        boundType = Resources.classForName(namespace);
      } catch (ClassNotFoundException e) {
        //ignore, bound type is not required
      }
      if (boundType != null) {
        if (!configuration.hasMapper(boundType)) {
          // Spring may not know the real resource name so we set a flag
          // to prevent loading again this resource from the mapper interface
          // look at MapperAnnotationBuilder#loadXmlResource
          configuration.addLoadedResource("namespace:" + namespace);
          configuration.addMapper(boundType);
        }
      }
    }
  }

方法是mybatis中的方法, 唯一不同的是, 這里的 configuration 不是mybatis中的那個 Configuration了, 而是 MybatisConfiguration.

所以 configuration.addMapper(boundType) 調用的就是 MybatisConfiguration.addMapper().

   //MybatisConfiguration.java   
   @Override
    public <T> void addMapper(Class<T> type) {
        //MybatisMapperRegistry  
        mybatisMapperRegistry.addMapper(type);
    }

    @Override
    public <T> void addMapper(Class<T> type) {
        if (type.isInterface()) {
            if (hasMapper(type)) {
                // TODO 如果之前注入 直接返回
                return;
                // throw new BindingException("Type " + type +
                // " is already known to the MybatisPlusMapperRegistry.");
            }
            boolean loadCompleted = false;
            try {
                knownMappers.put(type, new PageMapperProxyFactory<>(type));
                // It's important that the type is added before the parser is run
                // otherwise the binding may automatically be attempted by the
                // mapper parser. If the type is already known, it won't try.
                // TODO 自定義無 XML 注入
                MybatisMapperAnnotationBuilder parser = new MybatisMapperAnnotationBuilder(config, type);
                parser.parse();
                loadCompleted = true;
            } finally {
                if (!loadCompleted) {
                    knownMappers.remove(type);
                }
            }
        }
    }
Mybatis 在這里使用的是 MapperAnnotationBuilder. 
但是 MybatisMapperAnnotationBuilder 繼承自 Mybatis 的MapperAnnotationBuilder, 而且把里面的屬性拷貝了一份. 方法有部分修改.
接着看 MybatisMapperAnnotationBuilder.parse() 方法:
    @Override
    public void parse() {
        String resource = type.toString();
        if (!configuration.isResourceLoaded(resource)) {
            loadXmlResource();
            configuration.addLoadedResource(resource);
            assistant.setCurrentNamespace(type.getName());
            parseCache();
            parseCacheRef();
            Method[] methods = type.getMethods();
            // TODO 注入 CURD 動態 SQL (應該在注解之前注入)
       //--------------mybatis-plus 新增的部分---------- if (BaseMapper.class.isAssignableFrom(type)) { GlobalConfigUtils.getSqlInjector(configuration).inspectInject(assistant, type); }
       //---------------end --------------------------
for (Method method : methods) { try { // issue #237 if (!method.isBridge()) { parseStatement(method); } } catch (IncompleteElementException e) { configuration.addIncompleteMethod(new MethodResolver(this, method)); } } } parsePendingMethods(); }


1. GlobalConfigUtils.getSqlInjector()

    public static ISqlInjector getSqlInjector(Configuration configuration) {
        // fix #140
        GlobalConfig globalConfiguration = getGlobalConfig(configuration);
        ISqlInjector sqlInjector = globalConfiguration.getSqlInjector();
        if (sqlInjector == null) {
            sqlInjector = new DefaultSqlInjector();
            globalConfiguration.setSqlInjector(sqlInjector);
        }
        return sqlInjector;
    }

這里就是從配置中獲取自定義 ISqlInjector , 如 mybatis-plus 實例中的 CustomSqlInjector.

如果獲取不到自定義的, 則使用默認的  DefaultSqlInjector

 
        

2. ISqlInjector.inspectInject()

 //AbstractSqlInjector.java 抽象類, 實現 ISqlInjector 接口
@Override
public void inspectInject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) { String className = mapperClass.toString(); Set<String> mapperRegistryCache = GlobalConfigUtils.getMapperRegistryCache(builderAssistant.getConfiguration()); if (!mapperRegistryCache.contains(className)) { List<AbstractMethod> methodList = this.getMethodList(); Assert.notEmpty(methodList, "No effective injection method was found."); // 循環注入自定義方法 methodList.forEach(m -> m.inject(builderAssistant, mapperClass)); mapperRegistryCache.add(className); /** * 初始化 SQL 解析 */ if (GlobalConfigUtils.getGlobalConfig(builderAssistant.getConfiguration()).isSqlParserCache()) { SqlParserHelper.initSqlParserInfoCache(mapperClass); } } }

這里的 this.getMethodList() 調用的就是 DefaultSqlInjector 的 getMethodList() 方法:

public class DefaultSqlInjector extends AbstractSqlInjector {
    @Override
    public List<AbstractMethod> getMethodList() { return Stream.of( new Insert(), new Delete(), new DeleteByMap(), new DeleteById(), new DeleteBatchByIds(), new Update(), new UpdateById(), new SelectById(), new SelectBatchByIds(), new SelectByMap(), new SelectOne(), new SelectCount(), new SelectMaps(), new SelectMapsPage(), new SelectObjs(), new SelectList(), new SelectPage() ).collect(Collectors.toList()); } }

可以看到, 它返回了一個 sql操作方法集合. 

 

2.1 m.inject()

遍歷 List<AbstractMethod> , 調用其 inject() 方法.
    /**
     * 注入自定義方法
     */
    public void inject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) {
        this.configuration = builderAssistant.getConfiguration();
        this.builderAssistant = builderAssistant;
        this.languageDriver = configuration.getDefaultScriptingLanguageInstance();
        Class<?> modelClass = extractModelClass(mapperClass);
        if (null != modelClass) {
            /**
             * 注入自定義方法
             */
            TableInfo tableInfo = TableInfoHelper.initTableInfo(builderAssistant, modelClass);
            injectMappedStatement(mapperClass, modelClass, tableInfo);
        }
    }
injectMappedStatement 是一個抽象方法, 留給子類實現的. 說白了, 就是
Insert, Delete, DeleteByMap 等實現的. 其實就是根據 實體類和配置, 解析生成 sql 操作語句. 
具體細節, 留給后面再看吧.

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM