youyichannel

志于道,据于德,依于仁,游于艺!

0%

MyBatis3 — 初始化基本过程

MyBatis 和数据库的交互有 Java APIMapper 接口两种方式,所以 MyBatis 的初始化必然也有两种。

那么 MyBatis 是如何初始化的呢?

MyBatis 初始化方式

MyBatis 初始化可以有两种方式:

  • 基于 XML 配置文件:基于 XML 配置文件的方式是将 MyBatis 所有的配置信息放在 XML 文件中,MyBatis 通过加载 XML 配置文件,将配置文信息组装成内部的 Configuration 对象;
  • 基于 Java API:这种需要开发者在代码中手动创建 Configuration 对象,然后将配置参数设置进 Configuration 对象中。

初始化方式 — XML 配置

一个简单的例子:

// MyBatis 初始化
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

// 创建 SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();

// 执行 SQL 语句
List<Object> list = sqlSession.selectList("com.youyi.mybatis.dao.UserDao.getAll");
list.forEach(System.out::println);

总的来说,上述代码经历了三个阶段:

  1. MyBatis 初始化
  2. 创建 SqlSession
  3. 执行 SQL 语句

上述代码的功能是根据配置文件 mybatis-config.xml 配置文件,创建 SqlSessionFactory 对象,然后产生 SqlSession,执行 SQL 语句。其中 MyBatis 的初始化就发生在第三行代码,那么这行代码到底干了什么呢?

MyBatis 初始化基本流程

SqlSessionFactoryBuilder 根据传入的数据流生成 Configuration 对象,然后根据Configuration 对象创建默认的 SqlSessionFactory 实例。

由上图所示,Mybatis 初始化要经过以下几步:

  • 调用 SqlSessionFactoryBuilder 对象的 build(inputStream) 方法;
  • SqlSessionFactoryBuilder 会根据输入流 inputStream 等信息创建 XMLConfigBuilder对象;
  • SqlSessionFactoryBuilder 调用 XMLConfigBuilder 对象的 parse() 方法;
  • XMLConfigBuilder 对象返回 Configuration 对象;
  • SqlSessionFactoryBuilder 根据 Configuration 对象创建一个 DefaultSessionFactory 对象;
  • SqlSessionFactoryBuilder 返回 DefaultSessionFactory 对象给 Client,供 Client 使用。

相关的代码:

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

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
// 创建 XMLConfigBuilder 对象用来解析 XML 配置文件
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
// 将 XML 配置文件解析成 Configuration 对象传入 build 方法创建 SqlSessionFactory 对象
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
if (inputStream != null) {
inputStream.close();
}
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}

// 从此处可以看出,MyBatis 内部通过 Configuration 对象来创建 SqlSessionFactory,开发者也可以自己通过 API 构造 Configuration 对象,调用此方法创建 SqlSessionFactory
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}

上述初始化过程涉及到的对象:

  • SqlSessionFactoryBuilderSqlSessionFactory 的构造器,用于创建SqlSessionFactory,采用了建造者模式;
  • Configuration:该对象是 mybatis-config.xml 文件中所有 Mybatis 配置信息;
  • SqlSessionFactorySqlSession 工厂类,以工厂形式创建 SqlSession 对象,采用了工厂模式;
  • XmlConfigParser:负责将 mybatis-config.xml 配置文件解析成 Configuration 对象,提供给 SqlSessonFactoryBuilder 创建 SqlSessionFactory

创建 Configuration 对象的过程

当 SqlSessionFactoryBuilder 执行 build() 方法,调用了 XMLConfigBuilder 的 parse() 方法返回 Configuration 对象。那么 parse() 方法是如何处理 XML 文件生成 Configuration 对象的呢?

1)XMLConfigBuilder 会将 XML 配置文件信息转换为 Document 对象

XML 配置文件定义的文件 DTD(比如 mybatis-3-config.dtd)会转换成 XMLMapperEntityResolver 对象,然后和 XML 配置文件的 InputStream 一起被封装到 XpathParser 对象中,XpathParser 的作用是提供根据 Xpath 表达式获取基本的 DOM 节点信息的功能。

2)XMLConfigBuilder 调用 parse() 方法

XMLConfigBuilder 会从 XPathParser 中取出 <configuration> 节点对应的 Node 对象,然后解析此 Node 节点的子 Node,比如 properties, settings, typeAliases,typeHandlers, objectFactory, objectWrapperFactory, plugins, environments,databaseIdProvider, mappers

相关的代码:

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

// 解析 "/configuration" 节点下的子节点信息,然后将解析的结果设置到 Configuration 对象中
private void parseConfiguration(XNode root) {
try {
// issue #117 read properties first
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfsImpl(settings);
loadCustomLogImpl(settings);
typeAliasesElement(root.evalNode("typeAliases"));
pluginsElement(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"));
typeHandlersElement(root.evalNode("typeHandlers"));
mappersElement(root.evalNode("mappers")); // 重点操作
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}

在上述代码中,有一个非常重要的地方,就是解析 XML 配置文件子节点 <mappers> 的方法 mapperElements(root.evalNode("mappers")) , 它将解析我们配置的 Mapper.xml 配置文件。

Mapper 配置文件可以说是 MyBatis 的核心,MyBatis 的特性和理念都体现在此 Mapper 的配置和设计上。

3)将这些值解析出来设置到 Configuration 对象中

相关代码:

  • org.apache.ibatis.builder.xml.XMLConfigBuilder#environmentsElement
  • org.apache.ibatis.builder.xml.XMLConfigBuilder#settingsElement
  • ……

4)返回 Configuration 对象

初始化方式 — 基于 Java API

参考代码:

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

// 手动创建 XMLConfigBuilder,并创建 Configuration 对象
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, null, null);
Configuration configuration = parser.parse();

// 使用 Configuration 对象创建 SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);

// 使用 MyBatis
SqlSession sqlSession = sqlSessionFactory.openSession();
List<Object> list = sqlSession.selectList("com.youyi.mybatis.dao.UserDao.getAll");
list.forEach(System.out::println);