zoukankan      html  css  js  c++  java
  • Mybatis源码-sqlSessionFactory(一)

    第一步:读取配置文件 mybatis-config.xml 输入流
    第二步:根据输入流构建 SqlSessionFactory;

    一.流程图如下所示:

    二、代码剖析
    根据上面的时序图,我们分析根据源码分析每个步骤。
    ①、获取配置文件输入流

    InputStream inputStream = Resources.getResourceAsStream("mybatis.config.xml");

    这里没什么好说的,就是获取配置文件的输入流。
    ②、build(in)
    这里的 in 就是上一步获取的输入流 inputStream。

     public SqlSessionFactory build(InputStream inputStream) {
        return build(inputStream, null, null);
      }

    在进入到 build 方法:

      public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        try {
          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.
          }
        }
      }

    ③、XMLConfigBuilder(in)
    这一段代码是为了解析我们的配置文件,配置文件是 XML形式 ,我在之前的博客介绍过解析 XML 的几种方式。
    一种是基于树的结构来解析的称为DOM;另一种是基于事件流的形式称为SAX和(StAX)
    两者各有优缺点,我这里不做详细说明,想了解的可以看我之前的文章。
    而 Mybatis 使用的是 DOM 形式,并结合 XPath 来解析配置文件。
    ④、parse()

      public Configuration parse() {
        if (parsed) {
          throw new BuilderException("Each XMLConfigBuilder can only be used once.");
        }
        parsed = true;
        parseConfiguration(parser.evalNode("/configuration"));
        return configuration;
      }

    从 /configuration 标签处开始解析。然后我们进入到 this.parseConfiguration() 方法中:

      private void parseConfiguration(XNode root) {
        try {
          // issue #117 read properties first
          propertiesElement(root.evalNode("properties"));
          Properties settings = settingsAsProperties(root.evalNode("settings"));
          loadCustomVfs(settings);
          loadCustomLogImpl(settings);
          typeAliasesElement(root.evalNode("typeAliases"));
          pluginElement(root.evalNode("plugins"));
          objectFactoryElement(root.evalNode("objectFactory"));
          objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
          reflectorFactoryElement(root.evalNode("reflectorFactory"));
          settingsElement(settings);
          // read it after objectFactory and objectWrapperFactory issue #631
          environmentsElement(root.evalNode("environments"));
          databaseIdProviderElement(root.evalNode("databaseIdProvider"));
          typeHandlerElement(root.evalNode("typeHandlers"));
          mapperElement(root.evalNode("mappers"));
        } catch (Exception e) {
          throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
        }
      }

    看到这是不是很熟悉了,这不就是mybatis-config.xml 配置文件里面的各个标签名嘛,是的,这就是解析该文件,然后全部放在 configuration 对象中。需要注意的是,这里的 configuration 对象不仅包括 mybatis-config.xml 文件内容,也包括 xxxMapper.xml 文件内容。mybatis-config文件最多有11个配置项,分别是properties?, settings?, typeAliases?, typeHandlers?, objectFactory?, objectWrapperFactory?, reflectorFactory?, plugins?, environments?, databaseIdProvider?, mappers?,但是所有的配置都是可选的,这意味着mybatis-config配置文件本身可以什么都不包含。因为所有的配置最后保存到org.apache.ibatis.session.Configuration中,所以在详细查看每块配置的解析前,我们来看下Configuration的内部完整结构:

    public class Configuration {
    
      protected Environment environment;
      // 允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为false。默认为false
      protected boolean safeRowBoundsEnabled;
      // 允许在嵌套语句中使用分页(ResultHandler)。如果允许使用则设置为false。
      protected boolean safeResultHandlerEnabled = true;
      // 是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射。默认false
      protected boolean mapUnderscoreToCamelCase;
      // 当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载。默认值false (true in ≤3.4.1)
      protected boolean aggressiveLazyLoading;
      // 是否允许单一语句返回多结果集(需要兼容驱动)。
      protected boolean multipleResultSetsEnabled = true;
      // 允许 JDBC 支持自动生成主键,需要驱动兼容。这就是insert时获取mysql自增主键/oracle sequence的开关。注:一般来说,这是希望的结果,应该默认值为true比较合适。
      protected boolean useGeneratedKeys;
      // 使用列标签代替列名,一般来说,这是希望的结果
      protected boolean useColumnLabel = true;
      // 是否启用缓存
      protected boolean cacheEnabled = true;
      // 指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,这对于有 Map.keySet() 依赖或 null 值初始化的时候是有用的。
      protected boolean callSettersOnNulls;
      // 允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的工程必须采用Java 8编译,并且加上-parameters选项。(从3.4.1开始)
      protected boolean useActualParamName = true;
      //当返回行的所有列都是空时,MyBatis默认返回null。 当开启这个设置时,MyBatis会返回一个空实例。 请注意,它也适用于嵌套的结果集 (i.e. collectioin and association)。(从3.4.2开始) 注:这里应该拆分为两个参数比较合适, 一个用于结果集,一个用于单记录。通常来说,我们会希望结果集不是null,单记录仍然是null
      protected boolean returnInstanceForEmptyRow;
      // 指定 MyBatis 增加到日志名称的前缀。
      protected String logPrefix;
      // 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。一般建议指定为slf4j或log4j
      protected Class <? extends Log> logImpl;
      // 指定VFS的实现, VFS是mybatis提供的用于访问AS内资源的一个简便接口
      protected Class <? extends VFS> vfsImpl;
      // MyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询。 默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlSession 的不同调用将不会共享数据。
      protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
      // 当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。 某些驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。
      protected JdbcType jdbcTypeForNull = JdbcType.OTHER;
      // 指定对象的哪个方法触发一次延迟加载。
      protected Set<String> lazyLoadTriggerMethods = new HashSet<String>(Arrays.asList(new String[] { "equals", "clone", "hashCode", "toString" }));
      // 设置超时时间,它决定驱动等待数据库响应的秒数。默认不超时
      protected Integer defaultStatementTimeout;
      // 为驱动的结果集设置默认获取数量。
      protected Integer defaultFetchSize;
      // SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(prepared statements); BATCH 执行器将重用语句并执行批量更新。
      protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
      // 指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示取消自动映射;PARTIAL 只会自动映射没有定义嵌套结果集映射的结果集。 FULL 会自动映射任意复杂的结果集(无论是否嵌套)。
      protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL;
      // 指定发现自动映射目标未知列(或者未知属性类型)的行为。这个值应该设置为WARNING比较合适
      protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior = AutoMappingUnknownColumnBehavior.NONE;
      // settings下的properties属性
      protected Properties variables = new Properties();
      // 默认的反射器工厂,用于操作属性、构造器方便
      protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
      // 对象工厂, 所有的类resultMap类都需要依赖于对象工厂来实例化
      protected ObjectFactory objectFactory = new DefaultObjectFactory();
      // 对象包装器工厂,主要用来在创建非原生对象,比如增加了某些监控或者特殊属性的代理类
      protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
      // 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态。
      protected boolean lazyLoadingEnabled = false;
      // 指定 Mybatis 创建具有延迟加载能力的对象所用到的代理工具。MyBatis 3.3+使用JAVASSIST
      protected ProxyFactory proxyFactory = new JavassistProxyFactory(); // #224 Using internal Javassist instead of OGNL
      // MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId 属性。
      protected String databaseId;
    
      /**
       * Configuration factory class.
       * Used to create Configuration for loading deserialized unread properties.
       * 指定一个提供Configuration实例的类. 这个被返回的Configuration实例是用来加载被反序列化对象的懒加载属性值. 这个类必须包含一个签名方法static Configuration getConfiguration(). (从 3.2.3 版本开始)
       */
      protected Class<?> configurationFactory;
    
      protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
    
      // mybatis插件列表
      protected final InterceptorChain interceptorChain = new InterceptorChain();
      protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
    
      // 类型注册器, 用于在执行sql语句的出入参映射以及mybatis-config文件里的各种配置比如<transactionManager type="JDBC"/><dataSource type="POOLED">时使用简写, 后面会详细解释
      protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
      protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();
    
      protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");
      protected final Map<String, Cache> caches = new StrictMap<Cache>("Caches collection");
      protected final Map<String, ResultMap> resultMaps = new StrictMap<ResultMap>("Result Maps collection");
      protected final Map<String, ParameterMap> parameterMaps = new StrictMap<ParameterMap>("Parameter Maps collection");
      protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<KeyGenerator>("Key Generators collection");
    
      protected final Set<String> loadedResources = new HashSet<String>();
      protected final Map<String, XNode> sqlFragments = new StrictMap<XNode>("XML fragments parsed from previous mappers");
    
      protected final Collection<XMLStatementBuilder> incompleteStatements = new LinkedList<XMLStatementBuilder>();
      protected final Collection<CacheRefResolver> incompleteCacheRefs = new LinkedList<CacheRefResolver>();
      protected final Collection<ResultMapResolver> incompleteResultMaps = new LinkedList<ResultMapResolver>();
      protected final Collection<MethodResolver> incompleteMethods = new LinkedList<MethodResolver>();

    ⑤、build(configuration)

    public SqlSessionFactory build(Configuration config) {
        return new DefaultSqlSessionFactory(config);
      }

    就是去 new 了一个 DefaultSqlSessionFactory 对象,将 configuration 作为参数。
    ⑥、DefaultSqlSessionFactory(configuration)

        public DefaultSqlSessionFactory(Configuration configuration) {
            this.configuration = configuration;
        }

    三、总结
    自此,SqlSessionFactory 的创建过程就讲完了,总的来说就是一个封装了配置文件的工厂类。那么得到了 SqlSessionFactory 这个工厂对象,接下来干嘛?生产 SqlSession,然后通过 SqlSession 进行数据库的增删改查操作。

    参考博客:
    https://www.cnblogs.com/zhjh256/p/8512392.html
    https://mp.weixin.qq.com/s/d720FTEaEl__g4DSUu2HDA
    https://blog.csdn.net/ctycsdn/article/details/107012603

    郭慕荣博客园
  • 相关阅读:
    【ci框架】PHP常见面试题汇总。。。
    fieldset的collapse和expand事件
    CI 学习
    Ext JS 4预览:重构和规范渲染过程()
    LAMP环境搭建过程
    Extjs中FieldSet的收缩和展开实例
    EXTJS组件化(三)----组件之间的暧昧关系
    EXTJS组件化(四)---减少你的代码
    EXTJS组件化(二)----简易的私有和公有
    EXTJS组件化(一)----建立你的思想
  • 原文地址:https://www.cnblogs.com/jelly12345/p/15148373.html
Copyright © 2011-2022 走看看