当前位置:   article > 正文

Mybatis-SqlSession的创建过程_sqlsession创建

sqlsession创建

概述

SqlSession对象 表示 MyBaits框架 与 数据库 建立的会话
可以通过SqlSession实例 完成 对数据库的增删改查操作

为了简化流程描述,将SqlSession的创建过程拆解为3个阶段

  1. Configuration实例的创建过程
  2. SqlSessionFactory实例的创建过程
  3. SqlSession实例化的过程

XPath方式解析XML文件

MyBatis的主配置文件和Mapper配置都使用的是XML格式
MyBatis中的Configuration组件 用于 描述 主配置文件信息,框架 在启动时 会解析 XML配置,将 配置信息 转换为 Configuration对象

JDK API中提供了3种方式解析XML,分别为DOM、SAX和XPath
在这3种方式中,API最易于使用的就是XPath方式,MyBatis框架中也采用XPath方式解析XML文件中的配置信息

为了简化XPath解析操作,MyBatis通过XPathParser工具类 封装了 对XML的解析操作
同时使用XNode类增强了 对XML节点的操作
使用XNode对象,可以很方便地获取节点的属性、子节点等信息

Configuration实例创建过程

Configuration是MyBatis中比较重要的组件,主要有以下3个作用:

  • 用于描述MyBatis配置信息,例如<settings>标签配置的参数信息
  • 作为容器 注册(动词) MyBatis其他组件,例如TypeHandler、MappedStatement等
  • 提供工厂方法,创建ResultSetHandler、StatementHandler、Executor、ParameterHandler等组件实例

在SqlSession实例化前,首先解析 MyBatis主配置文件 及 所有Mapper文件,创建Configuration实例
MyBatis通过XMLConfigBuilder类 完成Configuration实例的构建工作

XMLConfigBuilder

XMLConfigBuilder继承了BaseBuilder,BaseBuilder中有Configuration成员变量
XMLConfigBuilder有许多构造方法,主要是两类,一类是字节流读取,一类是字符读取

  public XMLConfigBuilder(Reader reader, String environment, Properties props) {
    this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);
  }

  public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
    this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
  }

	// 最后走的都是这个方法
  private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
    super(new Configuration());
    ErrorContext.instance().resource("SQL Mapper Configuration");
    this.configuration.setVariables(props);
    this.parsed = false;
    this.environment = environment;
    this.parser = parser;
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

接下来通过parse方法返回Configuration实例

XMLConfigBuilder#parse

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


  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"));
      // 这里把解析后的root节点各种信息都set in Configuration属性
      settingsElement(settings);
      // read it after objectFactory and objectWrapperFactory issue #631
      environmentsElement(root.evalNode("environments"));
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      typeHandlerElement(root.evalNode("typeHandlers"));
      // set mapper信息inConfiguration属性中
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

MyBatis主配置文件中所有标签的用途如下

  • <properties>:用于配置属性信息,这些属性的值可以通过${…}方式引用
  • <settings>:通过一些属性来控制MyBatis运行时的一些行为。例如,指定日志实现、默认的Executor类型等
  • <typeAliases>:用于配置类型别名,目的是为Java类型设置一个更短的名字。它存在的意义仅在于用来减少类完全限定名的冗余
  • <plugins>:用于注册用户自定义的插件信息
  • <objectFactory>:MyBatis 通过 对象工厂(ObjectFactory)创建 参数对象 和 结果集映射对象,默认的对象工厂需要 做的仅仅是 实例化目标类,要么通过默认构造方法,要么 在参数映射存在的时候 通过 参数构造方法来实例化
  • <objectWrapperFactory>:MyBatis通过ObjectWrapperFactory创建ObjectWrapper对象,通过ObjectWrapper对象能够很方便地获取对象的属性、方法名等反射信息。标签用于配置用户自定义的ObjectWrapperFactory
  • <reflectorFactory>:MyBatis通过反射工厂(ReflectorFactory)创建 描述Java类型反射信息 的 Reflector对象,通过Reflector对象能够很方便地获取Class对象的Setter/Getter方法、属性等信息。标签用于配置自定义的反射工厂
  • <environments>:用于配置MyBatis数据连接相关的环境及事务管理器信息。通过该标签可以配置多个环境信息,然后指定具体使用哪个
  • <databaseIdProvider>:MyBatis能够根据不同的数据库厂商执行不同的SQL语句,该标签用于配置数据库厂商信息
  • <typeHandlers>:用于注册用户自定义的类型处理器(TypeHandler)
  • <mappers>:用于配置MyBatis Mapper信息

MyBatis框架启动后,首先创建Configuration实例,然后解析所有配置信息,将解析后的配置信息存放在Configuration实例中

注意:Configuration类中有mapperRegistry成员变量,mapperRegistry 会在后面执行Mapper过程中用到

protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
  • 1

SqlSession实例创建过程

XMLConfigBuilder#parse方法是被SqlSessionFactoryBuilder类在SqlSessionFactoryBuilder#build中调用的
SqlSessionFactoryBuilder#build返回一个SqlSessionFactory实例
然后SqlSessionFactory实例的openSession()方法创建一个SqlSession实例

MyBatis中的SqlSession实例使用工厂模式创建
在创建SqlSession实例 之前 需要 先创建 SqlSessionFactory工厂实例
然后调用SqlSessionFactory实例的openSession()方法,例如下面代码:

Reader reader = Resources.getResourceAsReader("xx/xx/xx/mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, props);
Configuration configuration = factory.getConfiguration();
  • 1
  • 2
  • 3

为了创建SqlSessionFactory实例,首先创建了一个SqlSessionFactoryBuilder实例
以MyBatis主配置文件输入流作为参数,调用SqlSessionFactoryBuilder实例的build()方法

SqlSessionFactoryBuilder

SqlSessionFactoryBuilder#build

build方法也主要有两类,一类是字符流,一类是字节
build方法中先构造XMLConfigBuilder实例
然后通过XMLConfigBuilder#parse方法得到一个Configuration实例
再使用build方法通过Configuration实例 得到一个 SqlSessionFactory实例

  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.
      }
    }
  }

  public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        reader.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }

  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

SqlSessionFactory接口只有一个默认的实现,即DefaultSqlSessionFactory
在上面的代码中,重载的build()方法中以Configuration实例作为参数,通过new关键字创建了一个DefaultSqlSessionFactory实例

DefaultSqlSessionFactory类中有各种openSession方法,可以得到一个SqlSession实例

DefaultSqlSessionFactory

DefaultSqlSessionFactory#openSession


  private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
    	 // 获取Mybatis主配置文件 配置的 环境信息
      final Environment environment = configuration.getEnvironment();
      // 创建 事务管理器工厂
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      // 创建 事务管理器
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      // 根据 Mybatis主配置文件中 制定的ExecutorType 创建对应的 Executor 实例
      final Executor executor = configuration.newExecutor(tx, execType);
      // 创建 DefaultSqlSession实例
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      closeTransaction(tx); // may have fetched a connection so lets call close()
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

  private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {
    try {
      boolean autoCommit;
      try {
        autoCommit = connection.getAutoCommit();
      } catch (SQLException e) {
        // Failover to true, as most poor drivers
        // or databases won't support transactions
        autoCommit = true;
      }
      final Environment environment = configuration.getEnvironment();
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      final Transaction tx = transactionFactory.newTransaction(connection);
      final Executor executor = configuration.newExecutor(tx, execType);
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

MyBatis提供了两种事务管理器,分别为JdbcTransaction和ManagedTransaction
JdbcTransaction 是使用 JDBC中的Connection对象 实现 事务管理的
ManagedTransaction表示 事务 由外部容器 管理

这两种事务管理器分别由对应的工厂类JdbcTransactionFactory和ManagedTransactionFactory创建

事务管理器对象创建完毕后,接着调用Configuration对象的newExecutor()方法,根据MyBatis主配置文件中指定的Executor类型创建对应的Executor对象
最后以Executor对象和Configuration对象作为参数,通过Java中的new关键字创建一个DefaultSqlSession对象

DefaultSqlSession对象中持有Executor对象的引用,真正执行SQL操作的是Executor对象

DefaultSqlSession

DefaultSqlSession#getMapper

  @Override
  public <T> T getMapper(Class<T> type) {
    return configuration.getMapper(type, this);
  }

  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

getMapper的创建及执行过程就是接下来要整理的内容

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/158238
推荐阅读
相关标签
  

闽ICP备14008679号