|
|
|
@ -1,6 +1,6 @@
|
|
|
|
|
和spring框架的IoC容器初始化一样,mybatis也会通过定位、解析相应的配置文件完成自己的初始化。mybatis的配置文件主要有mybatis-config.xml核心配置文件及一系列映射配置文件,另外,mybatis也会根据注解进行配置。
|
|
|
|
|
和 Spring框架 的 IoC容器初始化 一样,Mybatis 也会通过定位、解析相应的配置文件完成自己的初始化。Mybatis 的配置文件主要有 mybatis-config.xml核心配置文件 及一系列映射配置文件,另外,Mybatis 也会根据注解进行配置。
|
|
|
|
|
## 1 BaseBuilder
|
|
|
|
|
mybatis初始化的主要内容是加载并解析mybatis-config.xml配置文件、映射配置文件以及相关的注解信息。mybatis的初始化入口是SqlSessionFactoryBuilder的build()方法。
|
|
|
|
|
Mybatis初始化 的主要内容是加载并解析 mybatis-config.xml配置文件、映射配置文件以及相关的注解信息。Mybatis 的初始化入口是 SqlSessionFactoryBuilder 的 build()方法。
|
|
|
|
|
```java
|
|
|
|
|
public class SqlSessionFactoryBuilder {
|
|
|
|
|
|
|
|
|
@ -17,14 +17,14 @@ public class SqlSessionFactoryBuilder {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* build方法的主要实现
|
|
|
|
|
* build()方法 的主要实现
|
|
|
|
|
*/
|
|
|
|
|
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
|
|
|
|
|
try {
|
|
|
|
|
// SqlSessionFactory 会创建 XMLConfigBuilder对象 来解析 mybatis-config.xml配置文件
|
|
|
|
|
// XMLConfigBuilder 继承自 BaseBuilder抽象类,顾名思义这一系的类使用了 建造者设计模式
|
|
|
|
|
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
|
|
|
|
|
// 解析配置文件的内容 到Configuration对象,根据到Configuration对象
|
|
|
|
|
// 解析配置文件的内容 到 Configuration对象,根据 Configuration对象
|
|
|
|
|
// 创建 DefaultSqlSessionFactory对象,然后返回
|
|
|
|
|
return build(parser.parse());
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
@ -48,7 +48,7 @@ BaseBuilder中的核心字段如下:
|
|
|
|
|
```java
|
|
|
|
|
public abstract class BaseBuilder {
|
|
|
|
|
|
|
|
|
|
// 保存了mybatis的几乎所以核心配置信息,全局唯一
|
|
|
|
|
// 保存了 Mybatis 的几乎所以核心配置信息,全局唯一
|
|
|
|
|
protected final Configuration configuration;
|
|
|
|
|
// 在 mybatis-config.xml 中可以通过 <typeAliases>标签 定义别名
|
|
|
|
|
protected final TypeAliasRegistry typeAliasRegistry;
|
|
|
|
@ -115,7 +115,7 @@ public class XMLConfigBuilder extends BaseBuilder {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
mybatis中的标签很多,所以相对应的解析方法也很多,这里挑几个比较重要的标签进行分析。
|
|
|
|
|
Mybatis 中的标签很多,所以相对应的解析方法也很多,这里挑几个比较重要的标签进行分析。
|
|
|
|
|
### 2.1 解析<typeHandlers>标签
|
|
|
|
|
```java
|
|
|
|
|
private void typeHandlerElement(XNode parent) throws Exception {
|
|
|
|
@ -156,7 +156,7 @@ mybatis中的标签很多,所以相对应的解析方法也很多,这里挑
|
|
|
|
|
### 2.2 解析<environments>标签
|
|
|
|
|
```java
|
|
|
|
|
/**
|
|
|
|
|
* mybatis可以配置多个<environment>环境,分别用于开发、测试及生产等,
|
|
|
|
|
* Mybatis 可以配置多个 <environment>环境,分别用于开发、测试及生产等,
|
|
|
|
|
* 但每个 SqlSessionFactory实例 只能选择其一
|
|
|
|
|
*/
|
|
|
|
|
private void environmentsElement(XNode context) throws Exception {
|
|
|
|
@ -186,13 +186,13 @@ mybatis中的标签很多,所以相对应的解析方法也很多,这里挑
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
### 2.3 解析<databaseIdProvider>标签
|
|
|
|
|
mybatis不像hibernate那样,通过hql的方式直接帮助开发人员屏蔽不同数据库产品在sql语法上的差异,针对不同的数据库产品,mybatis往往要编写不同的sql语句。但在mybatis-config.xml配置文件中,可以通过<databaseIdProvider>定义所有支持的数据库产品的databaseId,然后在映射配置文件中定义sql语句节点时,通过databaseId指定该sql语句应用的数据库产品,也可以达到类似的屏蔽数据库产品的功能。
|
|
|
|
|
Mybatis 不像 Hibernate 那样,通过 HQL 的方式直接帮助开发人员屏蔽不同数据库产品在 sql语法 上的差异,针对不同的数据库产品, Mybatis 往往要编写不同的 sql语句。但在 mybatis-config.xml配置文件 中,可以通过 <databaseIdProvider> 定义所有支持的数据库产品的 databaseId,然后在映射配置文件中定义 sql语句节点 时,通过 databaseId 指定该 sql语句 应用的数据库产品,也可以达到类似的屏蔽数据库产品的功能。
|
|
|
|
|
|
|
|
|
|
mybatis初始化时,会根据前面解析到的DataSource来确认当前使用的数据库产品,然后在解析映射文件时,加载不带databaseId属性的sql语句 及 带有databaseId属性的sql语句,其中,带有databaseId属性的sql语句优先级更高,会被优先选中。
|
|
|
|
|
Mybatis 初始化时,会根据前面解析到的 DataSource 来确认当前使用的数据库产品,然后在解析映射文件时,加载不带 databaseId属性 的 sql语句 及带有 databaseId属性 的 sql语句,其中,带有 databaseId属性 的 sql语句 优先级更高,会被优先选中。
|
|
|
|
|
```java
|
|
|
|
|
/**
|
|
|
|
|
* 解析 <databaseIdProvider>节点,并创建指定的 DatabaseIdProvider对象,
|
|
|
|
|
* 该对象会返回databaseId的值,mybatis会根据databaseId选择对应的sql语句去执行
|
|
|
|
|
* 该对象会返回 databaseId的值,Mybatis 会根据 databaseId 选择对应的 sql语句 去执行
|
|
|
|
|
*/
|
|
|
|
|
private void databaseIdProviderElement(XNode context) throws Exception {
|
|
|
|
|
DatabaseIdProvider databaseIdProvider = null;
|
|
|
|
@ -217,7 +217,7 @@ mybatis初始化时,会根据前面解析到的DataSource来确认当前使用
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
mybatis提供了DatabaseIdProvider接口,该接口的核心方法为getDatabaseId(DataSource dataSource),主要根据dataSource查找对应的databaseId并返回。该接口的主要实现类为VendorDatabaseIdProvider。
|
|
|
|
|
Mybatis 提供了 DatabaseIdProvider接口,该接口的核心方法为 getDatabaseId(DataSource dataSource),主要根据 dataSource 查找对应的 databaseId 并返回。该接口的主要实现类为 VendorDatabaseIdProvider。
|
|
|
|
|
```java
|
|
|
|
|
public class VendorDatabaseIdProvider implements DatabaseIdProvider {
|
|
|
|
|
|
|
|
|
@ -280,7 +280,7 @@ public class VendorDatabaseIdProvider implements DatabaseIdProvider {
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
### 2.4 解析<mappers>标签
|
|
|
|
|
mybatis初始化时,除了加载mybatis-config.xml文件,还会加载全部的映射配置文件,mybatis-config.xml文件的<mapper>节点会告诉mybatis去哪里查找映射配置文件,及使用了配置注解标识的接口。
|
|
|
|
|
Mybatis 初始化时,除了加载 mybatis-config.xml文件,还会加载全部的映射配置文件,mybatis-config.xml 文件的 <mapper>节点 会告诉 Mybatis 去哪里查找映射配置文件,及使用了配置注解标识的接口。
|
|
|
|
|
```java
|
|
|
|
|
/**
|
|
|
|
|
* 解析 <mappers>节点,本方法会创建 XMLMapperBuilder对象 加载映射文件,如果映射配置文件存在
|
|
|
|
@ -297,7 +297,7 @@ mybatis初始化时,除了加载mybatis-config.xml文件,还会加载全部
|
|
|
|
|
configuration.addMappers(mapperPackage);
|
|
|
|
|
} else {
|
|
|
|
|
// 获取 <mapper>节点 的 resource、url、mapperClass属性,这三个属性互斥,只能有一个不为空
|
|
|
|
|
// mybatis提供了通过包名、映射文件路径、类全名、URL四种方式引入映射器。
|
|
|
|
|
// Mybatis 提供了通过包名、映射文件路径、类全名、URL 四种方式引入映射器。
|
|
|
|
|
// 映射器由一个接口和一个 XML配置文件 组成,XML文件 中定义了一个 命名空间namespace,
|
|
|
|
|
// 它的值就是接口对应的全路径。
|
|
|
|
|
String resource = child.getStringAttribute("resource");
|
|
|
|
@ -376,9 +376,9 @@ XMLMapperBuilder也根据配置文件进行了一系列节点解析,我们着
|
|
|
|
|
### 3.1 解析<resultMap>节点
|
|
|
|
|
select语句 查询得到的结果是一张二维表,水平方向上是一个个字段,垂直方向上是一条条记录。而 Java 是面向对象的程序设计语言,对象是根据类的定义创建的,类之间的引用关系可以认为是嵌套结构。JDBC编程 中,为了将结果集中的数据映射成 VO对象,我们需要自己写代码从结果集中获取数据,然后将数据封装成对应的 VO对象,并设置好对象之间的关系,这种 ORM 的过程中存在大量重复的代码。
|
|
|
|
|
|
|
|
|
|
mybatis通过<resultMap>节点定义了ORM规则,可以满足大部分的映射需求,减少重复代码,提高开发效率。
|
|
|
|
|
Mybatis 通过 <resultMap>节点 定义了 ORM规则,可以满足大部分的映射需求,减少重复代码,提高开发效率。
|
|
|
|
|
|
|
|
|
|
在分析<resultMap>节点的解析过程之前,先看一下该过程使用的数据结构。每个ResultMapping对象记录了结果集中的一列与JavaBean中一个属性之间的映射关系。<resultMap>节点下除了<discriminator>子节点的其它子节点 都会被解析成对应的ResultMapping对象。
|
|
|
|
|
在分析 <resultMap>节点 的解析过程之前,先看一下该过程使用的数据结构。每个 ResultMapping对象 记录了结果集中的一列与 JavaBean 中一个属性之间的映射关系。<resultMap>节点 下除了 <discriminator>子节点 的其它子节点,都会被解析成对应的 ResultMapping对象。
|
|
|
|
|
```java
|
|
|
|
|
public class ResultMapping {
|
|
|
|
|
|
|
|
|
@ -442,7 +442,7 @@ public class ResultMap {
|
|
|
|
|
private Boolean autoMapping;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
了解了ResultMapping 和ResultMap 记录的信息之后,下面开始介绍<resultMap>节点的解析过程。在XMLMapperBuilder中通过resultMapElements()方法解析映射配置文件中的全部<resultMap>节点,该方法会循环调用resultMapElement()方法处理每个<resultMap>节点。
|
|
|
|
|
了解了 ResultMapping 和 ResultMap 记录的信息之后,下面开始介绍 <resultMap>节点 的解析过程。在 XMLMapperBuilder 中通过 resultMapElements()方法 解析映射配置文件中的全部 <resultMap>节点,该方法会循环调用 resultMapElement()方法 处理每个 <resultMap> 节点。
|
|
|
|
|
```java
|
|
|
|
|
private ResultMap resultMapElement(XNode resultMapNode) throws Exception {
|
|
|
|
|
return resultMapElement(resultMapNode, Collections.<ResultMapping> emptyList());
|
|
|
|
@ -498,7 +498,7 @@ public class ResultMap {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
从上面的代码我们可以看到,mybatis从<resultMap>节点获取到id属性和type属性值之后,就会通过XMLMapperBuilder的buildResultMappingFromContext()方法为<result>节点创建对应的ResultMapping 对象。
|
|
|
|
|
从上面的代码我们可以看到,Mybatis 从 <resultMap>节点 获取到 id属性 和 type属性值 之后,就会通过 XMLMapperBuilder 的 buildResultMappingFromContext()方法 为 <result>节点 创建对应的 ResultMapping对象。
|
|
|
|
|
```java
|
|
|
|
|
/**
|
|
|
|
|
* 根据上下文环境构建 ResultMapping
|
|
|
|
@ -605,12 +605,12 @@ public class MapperBuilderAssistant extends BaseBuilder {
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
## 4 XMLStatementBuilder
|
|
|
|
|
这一部分看的不是很懂,暂时保留,日后深入理解了再写。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 5 绑定Mapper接口
|
|
|
|
|
通过之前对binding模块的解析可知,每个映射配置文件的命名空间可以绑定一个Mapper接口,并注册到MapperRegistry中。XMLMapperBuilder的bindMapperForNamespace()方法中,完成了映射配置文件与对应Mapper 接
|
|
|
|
|
口的绑定。
|
|
|
|
|
通过之前对 binding模块 的解析可知,每个映射配置文件的命名空间可以绑定一个 Mapper接口,并注册到 MapperRegistry中。XMLMapperBuilder 的 bindMapperForNamespace()方法 中,完成了映射配置文件与对应 Mapper接口 的绑定。
|
|
|
|
|
```java
|
|
|
|
|
public class XMLMapperBuilder extends BaseBuilder {
|
|
|
|
|
private void bindMapperForNamespace() {
|
|
|
|
|