Mybatis初始化详解

简介:

MyBatis 初始化的主要工作是加载井解析 mybatis-config.xml 配置文件、映射配置文件以及相关的注解信息。

mybatis-config.xml配置内容:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
     <!--数据库连接信息-->
    <properties resource="db.properties"></properties>
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING" />
    </settings>
    <environments default="development">
        <environment id="development">
            <!-- 事物管理器类型 -->
            <transactionManager type="JDBC"/>
            <!--连接池类型-->
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>
    <!--配置mapper映射文件-->
    <mappers>
        <mapper resource="CollegeMapper.xml"/>
    </mappers>
</configuration>

测试解析配置文件

public class MapperTest {
    public static void main(String[] args) throws IOException {
   		// 1 使用Resources加载全局配置文件,封装成抽象类Reader ,Resources 类正如其名,会帮助你从类路径下、文件系统或一个 web URL 中加载资源文件。
        Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        // 2 创建DefaultSqlSessionFactory
        SqlSessionFactory factory = builder.build(reader);
        // 3 创建会话SqlSession 
        SqlSession session = factory.openSession();
        // 获取Mapper对象
        CollegeMapper collegeMapper = session.getMapper(CollegeMapper.class);
        List<College> colleges = collegeMapper.selectCollegeAdminListAll();
        System.out.println(colleges);
        session.commit();
        session.close();
    }
}

运行MapperTest :

在这里插入图片描述
MyBatis的初始化步骤:

1 构建DefaultSqlSessionFactory对象
MyBatis的初始化入口是 SqlSessionFactoryBuilder.build()方法,该方法最终返回一个DefaultSqlSessionFactory对象。我们先来看下SqlSessionFactoryBuilder 类(部分方法已省略)的源码:

public class SqlSessionFactoryBuilder {
 // 构建DefaultSqlSessionFactory对象
  public SqlSessionFactory build(Reader reader) {
    return build(reader, null, null);
  }
// 构建DefaultSqlSessionFactory对象的具体执行方法
  public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    try {
      // 读取配置文件,创建XMLConfigBuilder 实例。
      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
      // 解析配置文件,创建DefaultSqlSessionFactory对象
      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.
      }
    }
  }
  //根据Configuration 创建DefaultSqlSessionFactory
  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

}

获取DefaultSqlSessionFactory步骤:

第一步:实例化XMLConfigBuilder 。
SqlSessionFactoryBuilder中使用XMLConfigBuilder 对象来解析mybatis-config.xml 配置文件 ,我们来看下XMLConfigBuilder结构。
XMLConfigBuilder 继承BaseBuilder,是mybatis专门用来解析全局配置文件的,而BaseBuilder跟mybatis初始化有着莫大的关系,我们先来看下BaseBuilder基本信息。

(1) BaseBuilder类图如下:
在这里插入图片描述
其中XMLMapperBuilder是解析 Mapper 映射器;XMLStatementBuilder用来解析增删改查标签。

(2)BaseBuilder的核心字段解释:
Configuration 是 MyBatis初始化过程的核心对象MyBatis 中几乎全部的配置信息会保存到Configuration 对象中。Configuration 类里面有很多的属性,有很多是跟 mybatis-config里面的标签直接对应的。
protected final Configuration configuration;
// 记录配置文件中的所有别名信息。
protected final TypeAliasRegistry typeAliasRegistry;
// 加载配置文件中自定义的 Type Handler
protected final TypeHandlerRegistry typeHandlerRegistry;

以上三个BaseBuilder的成员变量都是在 MyBatis 初始化过程中创建的全局唯一的对象。

第二步: XMLConfigBuilder 获取Configuration 对象

真正解析mybatis-config配置文件是通过XMLConfigBuilder.parse()方法,具体源码如下:

// 首先会检查是不是已经解析过,也就是说在应用的生命周期里面,mybatis-config配置文件只需要解析一次,生成的 Configuration 对象也会存在应用的整个生命周期中。
  public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    // 在 mybatis-config.xml 配置文件中查找<configuration>节点,并开始解析
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }

// 具体解析 mybatis-config.xml方法
  private void parseConfiguration(XNode root) {
     try {
 
    // 解析<properties>节点
    propertiesElement(root.evalNode("properties"));
    // 解析<settings>节点
    Properties settings = settingsAsProperties(root.evalNode("settings"));
    //设置 vfsimpl 字段。loadCustomVfs 是获取 Vitual File System 的自定义实现类,比如我们要读取本地文件,或者 FTP 远程文件的时候,就可以用到自定义的 VFS 类。我们根据<settings>标签里面的<vfsImpl>标签,生成了一个抽象类 VFS 的子类,并且赋值到 Configuration中。
    loadCustomVfs(settings);
    // loadCustomLogImpl 是根据<logImpl>标签获取日志的实现类,我们可以用到很多的日志的方案,包括 LOG4J,LOG4J2,SLF4J 等等。这里生成了一个 Log 接口的实现类,并且赋值到 Configuration 中。
    loadCustomLogImpl(settings);
     // 解析<typeAliases>节点
     typeAliasesElement(root.evalNode("typeAliases"));
     // 解析<plugins>节点。<plugins>标签里面只有<plugin>标签,<plugin>标签里面只有<property>标签。标签解析完以后,会生成一个 Interceptor 对象,并且添加到 Configuration 的InterceptorChain 属性里面,它是一个 List。
     pluginElement(root.evalNode("plugins"));
    // 解析<objectFactory>节点
    objectFactoryElement(root.evalNode("objectFactory"));
     // 解析<objectWrapperFactory>节点
    objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
    // 解析<reflectorFactory>节点
    reflectorFactoryElement(root.evalNode("reflectorFactory"));
    // 将 settings 佳设置到 Configuration 中
    settingsElement(settings);
    // read it after objectFactory and objectWrapperFactory issue #631
    // 解析<environments>节点。一个 environment 就是对应一个数据源,所以在这里我们会根据配置的<transactionManager>创建一个事务工厂,根据<dataSource>标签创建一个数据源,最后把这两个对象设置成 Environment 对象的属性,放到Configuration 里面。
    environmentsElement(root.evalNode("environments"));
    // 解析<databaseIdProvider>节点,生成 DatabaseIdProvider 对象(用来支持不同厂商的数据库)。
    databaseIdProviderElement(root.evalNode("databaseIdProvider"));
     // 解析<typeHandlers>节点。跟 TypeAlias 一样,TypeHandler 有两种配置方式,一种是单独配置一个类,一种是指定一个 package。最后我们得到的是 JavaType 和 JdbcType,以及用来做相互映射的 TypeHandler 之间的映射关系。最后存放在 TypeHandlerRegistry 对象里面。
    typeHandlerElement(root.evalNode("typeHandlers"));
    // 解析<mappers>节点,将包内的映射器接口实现全部注册为映射器。
    mapperElement(root.evalNode("mappers"));
  } catch (Exception e) {
  throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
 }

}

第三步:根据Configuration 创建DefaultSqlSessionFactory

创建DefaultSqlSessionFactory源码如下:

 //根据Configuration创建DefaultSqlSessionFactory
 public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
 }

创建DefaultSqlSessionFactory的流程图如下:

在这里插入图片描述

2 创建会话SqlSession

跟数据库的每一次连接,都需要创建一个会话,我们用openSession()方法来创建。具体的创建操作是由DefaultSqlSessionFactory. openSessionFromDataSource()来创建DefaultSqlSession对象。源码如下:

   public SqlSession openSession() {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
  }

// 创建SqlSession 具体实现方法。顾名思义就是从全局配置文件中的DataSource获取sqlSession。
 private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
    // 从 Configuration 里面拿到 Enviroment
      final Environment environment = configuration.getEnvironment();
      // 根据Enviroment获取事物工厂
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      // 创建Transaction 
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      // 创建Executor ,用来执行sql。
      final Executor executor = configuration.newExecutor(tx, execType);
      // 返回DefaultSqlSession实例,属性包括 Configuration、Executor 对象
      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();
    }
  }

根据源码,创建sqlsession可分为如下步骤:

第一步:创建事物 Transaction

Transaction 的类图结构:
Transaction
产生事物的工厂如下:
在这里插入图片描述
如果配置的是 JDBC,则会使用 Connection 对象的 commit()、rollback()、close()管理事务。
如果配置成 MANAGED,会把事务交给容器来管理,比如 JBOSS,Weblogic。因为我们跑的是本地程序,如果配置成 MANAGE 不会有任何事务。
如 果 是 Spring + MyBatis , 则 没 有 必 要 配 置 , 因 为 我 们 会 直 接 在applicationContext.xml 里面配置数据源和事务管理器,覆盖 MyBatis 的配置。

第二步:创建Executor执行器

Executor 的基本类型有三种:SIMPLE、BATCH、REUSE,默认是 SIMPLE(settingsElement()读取默认值),他们都继承了抽象类 BaseExecutor。类图结构如下:
在这里插入图片描述
三种类型的区别(通过 update()方法对比):
SimpleExecutor:每执行一次 update 或 select,就开启一个 Statement 对象,用完立刻关闭 Statement 对象。
ReuseExecutor:执行 update 或 select,以 sql 作为 key 查找 Statement 对象,存在就使用,不存在就创建,用完后,不关闭 Statement 对象,而是放置于 Map 内,供下一次使用。简言之,就是重复使用 Statement 对象。
BatchExecutor:执行 update(没有 select,JDBC 批处理不支持 select),将所有 sql 都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个 Statement 对象,每个 Statement 对象都是 addBatch()完毕后,等待逐一执行
executeBatch()批处理。与 JDBC 批处理相同。

如果全局配置文件配置了 cacheEnabled=ture,会用装饰器模式对 executor 进行包装:new CachingExecutor(executor)。
包装完毕后,会执行:executor = (Executor) interceptorChain.pluginAll(executor);对 executor 进行包装。

第三步:创建属性包括 Configuration、Executor 对象的DefaultSqlSession实例。

获取DefaultSqlSession源码如下:

 return new DefaultSqlSession(configuration, executor, autoCommit);
 
  public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
    this.configuration = configuration;
    this.executor = executor;
    this.dirty = false;
    this.autoCommit = autoCommit;
  }

创建DefaultSqlSession时序图如下:
在这里插入图片描述

总结:mybatis配置文件的解析大致分为两种 。一个是mybatis-config.xml 全局配置文件的解析。另外就是可能有很多个的 Mapper.xml 文件的解析,也包括在 Mapper 接口类上面定义的注解。本文只是讲解mybatis如何解析mybatis-config.xml,以及如何创建会话的过程,后面会对Mapper.xml解析,如何创建Mapper对象,以及sql如何执行进行详细讲解。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值