zoukankan      html  css  js  c++  java
  • mybatis-plus

    一. 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 操作语句. 
    具体细节, 留给后面再看吧.

  • 相关阅读:
    C++中合并两个排行榜的思路
    C++函数类型以及函数表实验
    C++获取两个值的差距比
    windows下的bash工具:winbash
    导入sql文件提示2006错误的解决办法
    C++延迟delete对象方案:采用unique_ptr托管欲删除的对象指针
    C++使用lower_bound快速查询分段配置
    sqlserver数据库操作
    判断 iframe 是否加载完成的完美方法
    SQL数据缓存依赖 [SqlServer | Cache | SqlCacheDependency ]
  • 原文地址:https://www.cnblogs.com/elvinle/p/12321429.html
Copyright © 2011-2022 走看看