zoukankan      html  css  js  c++  java
  • MyBatis-SqlSessionFactory 的创建(源码)

    Main 方法,mybatis 版本为 3.5.0

    解析配置文件的所有信息,保存在 Configuration 中,返回包含 Configuration 的 DefaultSqlSession

    MappedStatement:代表一个增删改查的详细信息

    InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    session = sqlSessionFactory.openSession();

    new SqlSessionFactoryBuilder().build(inputStream)

    org.apache.ibatis.session.SqlSessionFactoryBuilder

    /**
     * @param inputStream 参照 XML 文档或更特定的 SqlMapConfig.xml 文件的 InputStream 实例
     * @param environment 可选的参数,决定加载哪种环境(开发环境/生产环境),包括数据源和事务管理器
     * @param properties 可选的参数,使用 properties,就会加载那些 properties(属性配置文件),属性可以用 ${propName} 语法形式多次用在配置文件中。和 Spring 很像,一个思想?
     * @return
     */
    public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        try {
            //委托 XMLConfigBuilder 来解析 xml 文件,并构建
            XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
            return build(parser.parse());
        } catch (Exception e) {
            throw ExceptionFactory.wrapException("Error building SqlSession.", e);
        } finally {
            ErrorContext.instance().reset();
            try {
                inputStream.close();
            } catch (IOException e) {
                // Intentionally ignore. Prefer previous error.
            }
        }
    }
    
    /**
     * @param config Configuration
     * @return DefaultSqlSessionFactory
     */
    public SqlSessionFactory build(Configuration config) {
        return new DefaultSqlSessionFactory(config);
    }

    parser.parse()

    org.apache.ibatis.builder.xml.XMLConfigBuilder

    // 解析配置
    public Configuration parse() {
        // 如果已经解析过了,报错
        if (parsed) {
            throw new BuilderException("Each XMLConfigBuilder can only be used once.");
        }
        parsed = true;
        // XML 配置文件根节点是 configuration
        parseConfiguration(parser.evalNode("/configuration"));
        return configuration;
    }
    
    // 分步骤解析
    private void parseConfiguration(XNode root) {
        try {
            //issue #117 read properties first
            // 1.properties
            propertiesElement(root.evalNode("properties"));
            // 2.设置
            Properties settings = settingsAsProperties(root.evalNode("settings"));
            loadCustomVfs(settings);
            // 显式定义用什么log框架,不定义则用默认的自动发现 jar 包机制
            loadCustomLogImpl(settings);
            // 3.类型别名
            typeAliasesElement(root.evalNode("typeAliases"));
            // 4.插件
            pluginElement(root.evalNode("plugins"));
            // 5.对象工厂
            objectFactoryElement(root.evalNode("objectFactory"));
            // 6.对象包装工厂
            objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
            reflectorFactoryElement(root.evalNode("reflectorFactory"));
            settingsElement(settings);
            // read it after objectFactory and objectWrapperFactory issue #631
            // 7.环境
            environmentsElement(root.evalNode("environments"));
            // 8.数据库环境
            databaseIdProviderElement(root.evalNode("databaseIdProvider"));
            // 9.类型处理器
            typeHandlerElement(root.evalNode("typeHandlers"));
            // 10.SQL映射器
            mapperElement(root.evalNode("mappers"));
        } catch (Exception e) {
            throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
        }
    }
    
    // 设置
    private void settingsElement(Properties props) {
        // 如何自动映射列到字段属性
        configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
        // MyBatis 自动映射时未知列或未知属性处理策略,通过该配置可指定 MyBatis 在自动映射过程中遇到未知列或者未知属性时如何处理,默认不做任何处理
        configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE")));
        // 缓存
        configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
        // proxyFactory (CGLIB | JAVASSIST),延迟加载的核心技术就是用代理模式,CGLIB/JAVASSIST 两者选一
        configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));
        // 延迟加载
        configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
        // 延迟加载时,每种属性是否还要按需加载
        configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), false));
        // 允不允许多种结果集从一个单独 的语句中返回
        configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));
        // 使用列标签代替列名
        configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));
        // 允许 JDBC 支持生成的键
        configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));
        // 配置默认的执行器
        configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));
        // 超时时间
        configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));
        // 为驱动程序设置一个提示,以控制返回结果的获取大小。可以通过查询设置覆盖此参数值
        configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null));
        // 是否将DB字段自动映射到驼峰式Java属性(A_COLUMN-->aColumn)
        configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));
        // 嵌套语句上使用RowBounds
        configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));
        // 默认用session级别的缓存
        configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));
        // 为 null 值设置 jdbctype
        configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));
        // Object 的哪些方法将触发延迟加载
        configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));
        // 使用安全的 ResultHandler
        configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));
        // 动态 SQL 生成语言所使用的脚本语言
        configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));
        // 默认枚举类型处理器
        configuration.setDefaultEnumTypeHandler(resolveClass(props.getProperty("defaultEnumTypeHandler")));
        // 当结果集中含有 Null 值时是否执行映射对象的 setter 或者 Map 对象的 put 方法。此设置对于原始类型如 int,boolean 等无效
        configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
        // 允许使用方法签名中的名称作为语句参数名称。使用该特性,工程必须采用 Java 8 编译,并且加上-parameters选项。(从3.4.1开始)
        // 设置为true时传递参数需要使用 #{arg0}-#{argn}或者#{param1}-#{paramn},设置为false时 传递参数需要使用 #{0}-#{n}或者#{param1}-#{paramn}
        configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), true));
        // 当返回行的所有列都是空时,MyBatis默认返回null。当开启时,MyBatis 会返回一个空实例。它也适用于嵌套的结果集(从3.4.2开始)
        configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false));
        // logger 名字的前缀
        configuration.setLogPrefix(props.getProperty("logPrefix"));
        // 配置工厂
        configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
    }
    
    // 配置 SQL 映射器
    private void mapperElement(XNode parent) throws Exception {
        if (parent != null) {
            for (XNode child : parent.getChildren()) {
                if ("package".equals(child.getName())) {
                    // 自动扫描包下所有映射器
                    String mapperPackage = child.getStringAttribute("name");
                    configuration.addMappers(mapperPackage);
                } else {
                    String resource = child.getStringAttribute("resource");
                    String url = child.getStringAttribute("url");
                    String mapperClass = child.getStringAttribute("class");
                    if (resource != null && url == null && mapperClass == null) {
                        // 使用类路径
                        ErrorContext.instance().resource(resource);
                        InputStream inputStream = Resources.getResourceAsStream(resource);
                        // 映射器比较复杂,调用 XMLMapperBuilder,注意在 for 循环里每个 mapper 都重新 new 一个 XMLMapperBuilder 来解析
                        XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
                        mapperParser.parse();
                    } else if (resource == null && url != null && mapperClass == null) {
                        // 使用绝对 url 路径
                        ErrorContext.instance().resource(url);
                        InputStream inputStream = Resources.getUrlAsStream(url);
                        XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
                        mapperParser.parse();
                    } else if (resource == null && url == null && mapperClass != null) {
                        // 使用 java 类名
                        Class<?> mapperInterface = Resources.classForName(mapperClass);
                        // 直接把这个映射加入配置
                        configuration.addMapper(mapperInterface);
                    } else {
                        throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
                    }
                }
            }
        }
    }

    mapperElement(root.evalNode("mappers")) --->  mapperParser.parse()

    org.apache.ibatis.builder.xml.XMLMapperBuilder

    // 解析 SQL 映射文件
    public void parse() {
        // 如果没有加载过再加载,防止重复加载
        if (!configuration.isResourceLoaded(resource)) {
            // 配置 mapper
            configurationElement(parser.evalNode("/mapper"));
            // 标记一下,已经加载过了
            configuration.addLoadedResource(resource);
            // 绑定映射器到 namespace
            bindMapperForNamespace();
        }
    
        parsePendingResultMaps();
        parsePendingCacheRefs();
        parsePendingStatements();
    }
    
    private void configurationElement(XNode context) {
        try {
            // 1.配置 namespace
            String namespace = context.getStringAttribute("namespace");
            if (namespace == null || namespace.equals("")) {
                throw new BuilderException("Mapper's namespace cannot be empty");
            }
            builderAssistant.setCurrentNamespace(namespace);
            // 2.配置 cache-ref
            cacheRefElement(context.evalNode("cache-ref"));
            // 3.配置 cache
            cacheElement(context.evalNode("cache"));
            //4.配置 parameterMap (已经废弃,老式风格的参数映射)
            parameterMapElement(context.evalNodes("/mapper/parameterMap"));
            //5.配置 resultMap (高级功能)
            resultMapElements(context.evalNodes("/mapper/resultMap"));
            //6.配置 sql (定义可重用的 SQL 代码段)
            sqlElement(context.evalNodes("/mapper/sql"));
            //7.配置 select|insert|update|delete
            buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
        } catch (Exception e) {
            throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
        }
    }
    
    // 7.配置 select|insert|update|delete
    private void buildStatementFromContext(List<XNode> list) {
        if (configuration.getDatabaseId() != null) {
            buildStatementFromContext(list, configuration.getDatabaseId());
        }
        buildStatementFromContext(list, null);
    }
    
    // 7.1 构建语句
    private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
        for (XNode context : list) {
            // 构建所有语句,一个 mapper 下可以有很多 select,语句比较复杂,核心都在这里面,所以调用 XMLStatementBuilder
            final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
            try {
                // 核心 XMLStatementBuilder.parseStatementNode
                statementParser.parseStatementNode();
            } catch (IncompleteElementException e) {
                // 如果出现SQL语句不完整,把它记下来,放到 configuration 去
                configuration.addIncompleteStatement(statementParser);
            }
        }
    }
    
    private void bindMapperForNamespace() {
        // 获取 mapper 映射文件的命名空间
        String namespace = builderAssistant.getCurrentNamespace();
        if (namespace != null) {
            Class<?> boundType = null;
            try {
                // 创建 mapper 映射文件的代理接口,后续就可以 session.getMapper(xxxMapper.class)
                boundType = Resources.classForName(namespace);
            } catch (ClassNotFoundException e) {
                //ignore, bound type is not required
            }
            if (boundType != null) {
                // 将命名空间和代理接口添加至 configuration
                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);
                }
            }
        }
    }

    statementParser.parseStatementNode()

    org.apache.ibatis.builder.xml.XMLStatementBuilder

    // 解析语句(select|insert|update|delete)
    public void parseStatementNode() {
        String id = context.getStringAttribute("id");
        String databaseId = context.getStringAttribute("databaseId");
    
        // 如果databaseId不匹配,退出
        if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
            return;
        }
    
        // 驱动程序每次批量返回的结果行数
        Integer fetchSize = context.getIntAttribute("fetchSize");
        // 超时时间
        Integer timeout = context.getIntAttribute("timeout");
        // 引用外部 parameterMap,已废弃
        String parameterMap = context.getStringAttribute("parameterMap");
        // 参数类型
        String parameterType = context.getStringAttribute("parameterType");
        Class<?> parameterTypeClass = resolveClass(parameterType);
        // 引用外部的 resultMap(高级功能)
        String resultMap = context.getStringAttribute("resultMap");
        // 结果类型
        String resultType = context.getStringAttribute("resultType");
        // 脚本语言,mybatis3.2 的新功能
        String lang = context.getStringAttribute("lang");
        // 得到语言驱动
        LanguageDriver langDriver = getLanguageDriver(lang);
    
        Class<?> resultTypeClass = resolveClass(resultType);
        // 结果集类型,FORWARD_ONLY|SCROLL_SENSITIVE|SCROLL_INSENSITIVE 中的一种
        String resultSetType = context.getStringAttribute("resultSetType");
        // 语句类型, STATEMENT|PREPARED|CALLABLE 中的一种
        StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
        ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
    
        // 获取命令类型(select|insert|update|delete)
        String nodeName = context.getNode().getNodeName();
        SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
        boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
        boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
        // 是否要缓存 select 结果
        boolean useCache = context.getBooleanAttribute("useCache", isSelect);
        // 仅针对嵌套结果 select 语句适用:如果为 true,就是假设包含了嵌套结果集或是分组了,这样的话当返回一个主结果行的时候,就不会发生有对前面结果集的引用的情况
        // 这就使得在获取嵌套的结果集的时候不至于导致内存不够用。默认值:false
        boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);
    
        // Include Fragments before parsing
        // 解析之前先解析<include>SQL片段
        XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
        includeParser.applyIncludes(context.getNode());
    
        // Parse selectKey after includes and remove them.
        // 解析之前先解析<selectKey>
        processSelectKeyNodes(id, parameterTypeClass, langDriver);
    
        // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
        // 解析成SqlSource,一般是 DynamicSqlSource
        SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
        String resultSets = context.getStringAttribute("resultSets");
        // (仅对 insert 有用) 标记一个属性, MyBatis 会通过 getGeneratedKeys 或者通过 insert 语句的 selectKey 子元素设置它的值
        String keyProperty = context.getStringAttribute("keyProperty");
        // (仅对 insert 有用) 标记一个属性, MyBatis 会通过 getGeneratedKeys 或者通过 insert 语句的 selectKey 子元素设置它的值
        String keyColumn = context.getStringAttribute("keyColumn");
        KeyGenerator keyGenerator;
        String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
        keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
        if (configuration.hasKeyGenerator(keyStatementId)) {
            keyGenerator = configuration.getKeyGenerator(keyStatementId);
        } else {
            keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
                    configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
                    ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
        }
    
        // 调用助手类,最终添加到 configuration 的 mappedStatements 中
        builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
                fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
                resultSetTypeEnum, flushCache, useCache, resultOrdered,
                keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
    }

    时序图


    https://github.com/tuguangquan/mybatis/tree/master/src/main/java/org/apache/ibatis

  • 相关阅读:
    c# influxDB
    ASP.NET Web简单开发
    vue3 最长递增子序列 diff优化
    【转】Android Kotlin协程 coroutines 理解
    基于混合模型的语音降噪实践
    语音降噪论文“A Hybrid Approach for Speech Enhancement Using MoG Model and Neural Network Phoneme Classifier”的研读
    基于sinc的音频重采样(二):实现
    基于sinc的音频重采样(一):原理
    深度学习中神经网络模型的量化
    嵌入式设备上卷积神经网络推理时memory的优化
  • 原文地址:https://www.cnblogs.com/jhxxb/p/10557641.html
Copyright © 2011-2022 走看看