天天看點

Mybatis-SqlSession的建立過程概述XPath方式解析XML檔案Configuration執行個體建立過程XMLConfigBuilderSqlSession執行個體建立過程SqlSessionFactoryBuilderDefaultSqlSessionFactoryDefaultSqlSession

SqlSession的建立過程

  • 概述
  • XPath方式解析XML檔案
  • Configuration執行個體建立過程
  • XMLConfigBuilder
    • XMLConfigBuilder#parse
  • SqlSession執行個體建立過程
  • SqlSessionFactoryBuilder
    • SqlSessionFactoryBuilder#build
  • DefaultSqlSessionFactory
    • DefaultSqlSessionFactory#openSession
  • DefaultSqlSession
    • DefaultSqlSession#getMapper

概述

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

接下來通過

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);
    }
  }
           

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過程中用到

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();
           

為了建立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);
  }
           

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();
    }
  }
           

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);
  }
           

getMapper的建立及執行過程就是接下來要整理的内容

繼續閱讀