MyBatis原理深入解析(一)
1 引言
本文主要讲解JDBC
怎么演变到Mybatis
的渐变过程,重点讲解了为什么要将JDBC
封装成Mybaits
这样一个持久层框架。再而论述Mybatis
作为一个数据持久层框架本身有待改进之处。
2 JDBC
实现查询分析
我们先看看我们最熟悉也是最基础的通过JDBC
查询数据库数据,一般需要以下七个步骤:
- 加载
JDBC
驱动; - 建立并获取数据库连接;
- 创建
JDBC Statements
对象; - 设置
SQL
语句的传入参数; - 执行
SQL
语句并获得查询结果; - 对查询结果进行转换处理并将处理结果返回;
- 释放相关资源(关闭
Connection
,关闭Statement
,关闭ResultSet
);
以下是具体的实现代码:
1
|
public static List<Map<String,Object>> queryForList(){
|
3 JDBC
演变到Mybatis
过程
上面我们看到了实现JDBC
有七个步骤,哪些步骤是可以进一步封装的,减少我们开发的代码量。
3.1 第一步优化:连接获取和释放
1.问题描述:
数据库连接频繁的开启和关闭本身就造成了资源的浪费,影响系统的性能。
解决问题:
数据库连接的获取和关闭我们可以使用数据库连接池来解决资源浪费的问题。通过连接池就可以反复利用已经建立的连接去访问数据库了。减少连接的开启和关闭的时间。
2.问题描述:
但是现在连接池多种多样,可能存在变化,有可能采用DBCP
的连接池,也有可能采用容器本身的JNDI
数据库连接池。
解决问题:
我们可以通过
DataSource
进行隔离解耦,我们统一从DataSource
里面获取数据库连接,DataSource
具体由DBCP
实现还是由容器的JNDI
实现都可以,所以我们将DataSource
的具体实现通过让用户配置来应对变化。
3.2 第二步优化:SQL
统一存取
1.问题描述:
我们使用JDBC
进行操作数据库时,SQL
语句基本都散落在各个JAVA
类中,这样有三个不足之处:
第一,可读性很差,不利于维护以及做性能调优。
第二,改动
Java
代码需要重新编译、打包部署。第三,不利于取出
SQL
在数据库客户端执行(取出后还得删掉中间的Java
代码,编写好的SQL
语句写好后还得通过+
号在Java
进行拼凑)。
解决问题:
我们可以考虑不把
SQL
语句写到Java
代码中,那么把SQL
语句放到哪里呢?首先需要有一个统一存放的地方,我们可以将这些SQL
语句统一集中放到配置文件或者数据库里面(以key-value
的格式存放)。然后通过SQL
语句的key
值去获取对应的SQL
语句。既然我们将
SQL
语句都统一放在配置文件或者数据库中,那么这里就涉及一个SQL
语句的加载问题。
3.3 第三步优化:传入参数映射和动态SQL
1. 问题描述:
很多情况下,我们都可以通过在SQL
语句中设置占位符来达到使用传入参数的目的,这种方式本身就有一定局限性,它是按照一定顺序传入参数的,要与占位符一一匹配。但是,如果我们传入的参数是不确定的(比如列表查询,根据用户填写的查询条件不同,传入查询的参数也是不同的,有时是一个参数、有时可能是三个参数),那么我们就得在后台代码中自己根据请求的传入参数去拼凑相应的SQL
语句,这样的话还是避免不了在Java
代码里面写SQL
语句的命运。既然我们已经把SQL
语句统一存放在配置文件或者数据库中了,怎么做到能够根据前台传入参数的不同,动态生成对应的SQL
语句呢?
解决问题:
第一,我们先解决这个动态问题,按照我们正常的程序员思维是,通过
if
和else
这类的判断来进行是最直观的,这个时候我们想到了JSTL
中的<if test=""></if>
这样的标签,那么,能不能将这类的标签引入到SQL
语句中呢?假设可以,那么我们这里就需要一个专门的SQL
解析器来解析这样的SQL
语句,但是,if
判断的变量来自于哪里呢?传入的值本身是可变的,那么我们得为这个值定义一个不变的变量名称,而且这个变量名称必须和对应的值要有对应关系,可以通过这个变量名称找到对应的值,这个时候我们想到了key-value
的Map
。解析的时候根据变量名的具体值来判断。假如前面可以判断没有问题,那么假如判断的结果是
true
,那么就需要输出的标签里面的SQL
片段,但是怎么解决在标签里面使用变量名称的问题呢?这里我们需要使用一种有别于SQL
的语法来嵌入变量(比如使用#变量名#
)。这样,SQL
语句经过解析后就可以动态的生成符合上下文的SQL
语句。还有,怎么区分开占位符变量和非占位变量?有时候我们单单使用占位符是满足不了的,占位符只能为查询条件占位,SQL语句其他地方使用不了。这里我们可以使用
#变量名#
表示占位符变量,使用$变量名$
表示非占位符变量。
3.4 第四步优化:结果映射和结果缓存
1.问题描述:
执行SQL
语句、获取执行结果、对执行结果进行转换处理、释放相关资源是一整套下来的。假如是执行查询语句,那么执行SQL
语句后,返回的是一个ResultSet
结果集,这个时候我们就需要将ResultSet
对象的数据取出来,不然等到释放资源时就取不到这些结果信息了。我们从前面的优化来看,以及将获取连接、设置传入参数、执行SQL
语句、释放资源这些都封装起来了,只剩下结果处理这块还没有进行封装,如果能封装起来,每个数据库操作都不用自己写那么一大堆Java
代码,直接调用一个封装的方法就可以搞定了。
解决问题:
我们分析一下,一般对执行结果的有哪些处理,有可能将结果不做任何处理就直接返回,也有可能将结果转换成一个
JavaBean
对象返回、一个Map
返回、一个List
返回等,结果处理可能是多种多样的。从这里看,我们必须告诉SQL
处理器两点:第一,需要返回什么类型的对象;第二,需要返回的对象的数据结构怎么跟执行的结果映射,这样才能将具体的值copy
到对应的数据结构上。接下来,我们可以进而考虑对
SQL
执行结果的缓存来提升性能。缓存数据都是key-value
的格式,那么这个key
怎么来呢?怎么保证唯一呢?即使同一条SQL
语句几次访问的过程中由于传入参数的不同,得到的执行SQL
语句也是不同的。那么缓存起来的时候是多对。但是SQL
语句和传入参数两部分合起来可以作为数据缓存的key
值。
3.5 第五步优化:解决重复SQL
语句问题
1.问题描述:
由于我们将所有SQL
语句都放到配置文件中,这个时候会遇到一个SQL
重复的问题,几个功能的SQL
语句其实都差不多,有些可能是SELECT
后面那段不同、有些可能是WHERE
语句不同。有时候表结构改了,那么我们就需要改多个地方,不利于维护。
解决问题:
当我们的代码程序出现重复代码时怎么办?将重复的代码抽离出来成为独立的一个类,然后在各个需要使用的地方进行引用。对于SQL
重复的问题,我们也可以采用这种方式,通过将SQL
片段模块化,将重复的SQL
片段独立成一个SQL
块,然后在各个SQL
语句引用重复的SQL
块,这样需要修改时只需要修改一处即可。
4. Mybaits
有待改进之处
1.问题描述:
Mybaits
所有的数据库操作都是基于SQL
语句,导致什么样的数据库操作都要写SQL
语句。一个应用系统要写的SQL
语句实在太多了。
改进方法:
我们对数据库进行的操作大部分都是对表数据的增删改查,很多都是对单表的数据进行操作,由这点我们可以想到一个问题:单表操作可不可以不写SQL
语句,通过JavaBean
的默认映射器生成对应的SQL
语句,比如:一个类UserInfo
对应于USER_INFO
表, userId
属性对应于USER_ID
字段。这样我们就可以通过反射可以获取到对应的表结构了,拼凑成对应的SQL语句显然不是问题。
5 MyBatis
框架整体设计
5.1 接口层-和数据库交互的方式
MyBatis
和数据库的交互有两种方式:
- 使用传统的
MyBatis
提供的API;- 使用
Mapper
接口;
5.1.1 使用传统的MyBatis
提供的API
这是传统的传递StatementId
和查询参数给SqlSession
对象,使用SqlSession
对象完成和数据库的交互;MyBatis
提供了非常方便和简单的API
,供用户实现对数据库的增删改查数据操作,以及对数据库连接信息和MyBatis
自身配置信息的维护操作。
上述使用MyBatis
的方法,是创建一个和数据库打交道的SqlSession
对象,然后根据StatementId
和参数来操作数据库,这种方式固然很简单和实用,但是它不符合面向对象语言的概念和面向接口编程的编程习惯。由于面向接口的编程是面向对象的大趋势,MyBatis
为了适应这一趋势,增加了第二种使用MyBatis
支持接口(Interface
)调用方式。
5.1.2 使用Mapper
接口
MyBatis 将配置文件中的每一个<mapper>
节点抽象为一个Mapper
接口:
这个接口中声明的方法和
<mapper>
节点中的<select|update|delete|insert>
节点项对应,即<select|update|delete|insert>
节点的id
值为Mapper
接口中的方法名称,parameterType
值表示Mapper
对应方法的入参类型,而resultMap
值则对应了Mapper
接口表示的返回值类型或者返回结果集的元素类型。
根据
MyBatis
的配置规范配置好后,通过SqlSession.getMapper(XXXMapper.class)
方法,MyBatis
会根据相应的接口声明的方法信息,通过动态代理机制生成一个Mapper
实例,我们使用Mapper
接口的某一个方法时,MyBatis
会根据这个方法的方法名和参数类型,确定StatementId
,底层还是通过SqlSession.select("statementId",parameterObject);
或者SqlSession.update("statementId",parameterObject);
等等来实现对数据库的操作,MyBatis
引用Mapper
接口这种调用方式,纯粹是为了满足面向接口编程的需要。(其实还有一个原因是在于,面向接口的编程,使得用户在接口上可以使用注解来配置SQL
语句,这样就可以脱离XML
配置文件,实现”0配置”)。
5.2 数据处理层
数据处理层可以说是MyBatis
的核心,从大的方面上讲,它要完成两个功能:
- 通过传入参数构建动态
SQL
语句;SQL
语句的执行以及封装查询结果集成List<E>
;
5.2.1 参数映射和动态SQL
语句生成
动态语句生成可以说是MyBatis
框架非常优雅的一个设计,MyBatis
通过传入的参数值,使用Ognl
来动态地构造SQL
语句,使得MyBatis
有很强的灵活性和扩展性。
参数映射指的是对于java
数据类型和jdbc
数据类型之间的转换:这里有包括两个过程:查询阶段,我们要将java
类型的数据,转换成jdbc
类型的数据,通过preparedStatement.setXXX()
来设值;另一个就是对resultSet
查询结果集的jdbcType
数据转换成java
数据类型。
5.2.2 SQL
语句的执行以及封装查询结果集成List<E>
动态SQL
语句生成之后,MyBatis
将执行SQL
语句,并将可能返回的结果集转换成List<E>
列表。MyBatis
在对结果集的处理中,支持结果集关系一对多和多对一的转换,并且有两种支持方式,一种为嵌套查询语句的查询,还有一种是嵌套结果集的查询。
5.3 框架支撑层
-
事务管理机制
事务管理机制对于ORM
框架而言是不可缺少的一部分,事务管理机制的质量也是考量一个ORM
框架是否优秀的一个标准。 -
连接池管理机制
由于创建一个数据库连接所占用的资源比较大,对于数据吞吐量大和访问量非常大的应用而言,连接池的设计就显得非常重要。 -
缓存机制
为了提高数据利用率和减小服务器和数据库的压力,MyBatis
会对于一些查询提供会话级别的数据缓存,会将对某一次查询,放置到SqlSession
中,在允许的时间间隔内,对于完全相同的查询,MyBatis
会直接将缓存结果返回给用户,而不用再到数据库中查找。 -
SQL
语句的配置方式
传统的MyBatis
配置SQL
语句方式就是使用XML
文件进行配置的,但是这种方式不能很好地支持面向接口编程的理念,为了支持面向接口的编程,MyBatis
引入了Mapper
接口的概念,面向接口的引入,对使用注解来配置SQL
语句成为可能,用户只需要在接口上添加必要的注解即可,不用再去配置XML
文件了,但是,目前的MyBatis
只是对注解配置SQL
语句提供了有限的支持,某些高级功能还是要依赖XML
配置文件配置SQL
语句。
5.4 引导层
引导层是配置和启动MyBatis
配置信息的方式。MyBatis
提供两种方式来引导MyBatis
:基于XML
配置文件的方式和基于Java API
的方式。
5.5 主要构件及其相互关系
从MyBatis
代码实现的角度来看,MyBatis
的主要的核心部件有以下几个:
SqlSession
:作为MyBatis
工作的主要顶层API
,表示和数据库交互的会话,完成必要数据库增删改查功能;
Executor
:MyBatis
执行器,是MyBatis
调度的核心,负责SQL
语句的生成和查询缓存的维护;
StatementHandler
:封装了JDBC Statement
操作,负责对JDBC statement
的操作,如设置参数、将Statement
结果集转换成List
集合。
ParameterHandler
:负责对用户传递的参数转换成JDBC Statement
所需要的参数;
ResultSetHandler
:负责将JDBC
返回的ResultSet
结果集对象转换成List
类型的集合;
TypeHandler
:负责java
数据类型和jdbc
数据类型之间的映射和转换;
MappedStatement
:MappedStatement
维护了一条<select|update|delete|insert>
节点的封装;
SqlSource
:负责根据用户传递的parameterObject
,动态地生成SQL
语句,将信息封装到BoundSql
对象中,并返回;
BoundSql
:表示动态生成的SQL
语句以及相应的参数信息;
Configuration
:MyBatis
所有的配置信息都维持在Configuration
对象之中;
它们的关系如下图所示:
6. SqlSession
工作过程分析
1. 开启一个数据库访问会话—创建SqlSession
对象
1
|
SqlSession sqlSession = factory.openSession();
|
2. 为SqlSession
传递一个配置的Sql
语句的StatementId
和参数,然后返回结果:
1
|
List<Employee> result = sqlSession.selectList("com.louis.mybatis.dao.EmployeesMapper.selectByMinSalary",params);
|
上述的
com.louis.mybatis.dao.EmployeesMapper.selectByMinSalary
,是配置在EmployeesMapper.xml
的StatementId
,params
是传递的查询参数。
让我们来看一下sqlSession.selectList()
方法的定义:
1
|
public <E> List<E> selectList(String statement, Object parameter) {
|
MyBatis
在初始化的时候,会将MyBatis
的配置信息全部加载到内存中,使用org.apache.ibatis.session.Configuration
实例来维护。使用者可以使用sqlSession.getConfiguration()
方法来获取。MyBatis
的配置文件中配置信息的组织格式和内存中对象的组织格式几乎完全对应的。
上述例子中的:
1
|
<select id="selectByMinSalary" resultMap="BaseResultMap" parameterType="java.util.Map" >
|
加载到内存中会生成一个对应的
MappedStatement
对象,然后会以key="com.louis.mybatis.dao.EmployeesMapper.selectByMinSalary"
,value为MappedStatement
对象的形式维护到Configuration
的一个Map
中。当以后需要使用的时候,只需要通过Id
值来获取就可以了。
从上述的代码中我们可以看到SqlSession
的职能是:SqlSession
根据StatementId
, 在Mybatis
配置对象Configuration
中获取到对应的MappedStatement
对象,然后调用Mybatis
执行器来执行具体的操作。
3. MyBatis
执行器Executor
根据SqlSession
传递的参数执行query()方法
(由于代码过长,读者只需阅读我注释的地方即可):
1
|
/**
|
上述的Executor.query()
方法几经转折,最后会创建一个StatementHandler
对象,然后将必要的参数传递给StatementHandler
,使用StatementHandler
来完成对数据库的查询,最终返回List
结果集。
从上面的代码中我们可以看出,Executor
的功能和作用是:
- 根据传递的参数,完成
SQL
语句的动态解析,生成BoundSql
对象,供StatementHandler
使用;- 为查询创建缓存,以提高性能;
- 创建
JDBC
的Statement
连接对象,传递给StatementHandler
对象,返回List
查询结果;
4. StatementHandler
对象负责设置Statement
对象中的查询参数、处理JDBC
返回的resultSet
,将resultSet
加工为List
集合返回:
接着上面的Executor
第六步,看一下:prepareStatement()
方法的实现:
1
|
/**
|
以上我们可以总结StatementHandler
对象主要完成两个工作:
- 对于
JDBC
的PreparedStatement
类型的对象,创建的过程中,我们使用的是SQL
语句字符串会包含若干个?
占位符,我们其后再对占位符进行设值。StatementHandler
通过parameterize(statement)
方法对Statement
进行设值;- StatementHandler通过
List<E> query(Statement statement, ResultHandler resultHandler)
方法来完成执行Statement
,和将Statement
对象返回的resultSet
封装成List
;
5. StatementHandler
的parameterize(statement)
方法的实现:
1
|
/**
|
从上述的代码可以看到,
StatementHandler
的parameterize(Statement)
方法调用了ParameterHandler
的setParameters(statement)
方法,ParameterHandler
的setParameters(Statement)
方法负责 根据我们输入的参数,对Statement
对象的?
占位符处进行赋值。
6. StatementHandler
的List<E> query(Statement statement, ResultHandler resultHandler)
方法的实现:
1
|
/**
|
从上述代码我们可以看出,
StatementHandler
的List<E> query(Statement statement, ResultHandler resultHandler)
方法的实现,是调用了ResultSetHandler
的handleResultSets(Statement)
方法。ResultSetHandler
的handleResultSets(Statement)
方法会将Statement
语句执行后生成的resultSet
结果集转换成List<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
35/**
* ResultSetHandler类的handleResultSets()方法实现
*
*/
public List<Object> handleResultSets(Statement stmt) throws SQLException {
final List<Object> multipleResults = new ArrayList<Object>();
int resultSetCount = 0;
ResultSetWrapper rsw = getFirstResultSet(stmt);
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
validateResultMapsCount(rsw, resultMapCount);
while (rsw != null && resultMapCount > resultSetCount) {
ResultMap resultMap = resultMaps.get(resultSetCount);
//将resultSet
handleResultSet(rsw, resultMap, multipleResults, null);
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
String[] resultSets = mappedStatement.getResulSets();
if (resultSets != null) {
while (rsw != null && resultSetCount < resultSets.length) {
ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
if (parentMapping != null) {
String nestedResultMapId = parentMapping.getNestedResultMapId();
ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
handleResultSet(rsw, resultMap, null, parentMapping);
}
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
}
return collapseSingleResultList(multipleResults);
}
7 MyBatis
初始化机制
7.1 MyBatis
的初始化做了什么
任何框架的初始化,无非是加载自己运行时所需要的配置信息。MyBatis
的配置信息,大概包含以下信息,其高层级结构如下:
MyBatis
的上述配置信息会配置在XML
配置文件中,那么,这些信息被加载进入MyBatis
内部,MyBatis
是怎样维护的呢?
MyBatis
采用了一个非常直白和简单的方式—使用org.apache.ibatis.session.Configuration
对象作为一个所有配置信息的容器,Configuration
对象的组织结构和XML
配置文件的组织结构几乎完全一样(当然,Configuration
对象的功能并不限于此,它还负责创建一些MyBatis
内部使用的对象,如Executor
等,这将在后续的文章中讨论)。如下图所示:
MyBatis
根据初始化好Configuration
信息,这时候用户就可以使用MyBatis
进行数据库操作了。可以这么说,MyBatis
初始化的过程,就是创建 Configuration
对象的过程。
MyBatis
的初始化可以有两种方式:
基于
XML
配置文件:基于XML
配置文件的方式是将MyBatis
的所有配置信息放在XML
文件中,MyBatis
通过加载XML
配置文件,将配置文信息组装成内部的Configuration
对象。基于
Java API
:这种方式不使用XML
配置文件,需要MyBatis
使用者在Java
代码中,手动创建Configuration
对象,然后将配置参数set
进Configuration
对象中。
接下来我们将通过基于XML
配置文件方式的MyBatis
初始化,深入探讨MyBatis
是如何通过配置文件构建Configuration
对象,并使用它。
7.2 基于XML
配置文件创建Configuration
对象
现在就从使用MyBatis
的简单例子入手,深入分析一下MyBatis
是怎样完成初始化的,都初始化了什么。看以下代码:
1
|
String resource = "mybatis-config.xml";
|
有过MyBatis
使用经验的读者会知道,上述语句的作用是执行com.foo.bean.BlogMapper.queryAllBlogInfo
定义的SQL
语句,返回一个List
结果集。总的来说,上述代码经历了Mybatis
初始化 –>创建SqlSession
–>执行SQL
语句返回结果三个过程。
上述代码的功能是根据配置文件mybatis-config.xml
配置文件,创建SqlSessionFactory
对象,然后产生SqlSession
,执行SQL语句。而Mybatis
的初始化就发生在第三句:SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
现在就让我们看看第三句到底发生了什么。
1. MyBatis
初始化基本过程:
SqlSessionFactoryBuilder
根据传入的数据流生成Configuration
对象,然后根据Configuration
对象创建默认的SqlSessionFactory
实例。
由上图所示,Mybatis
初始化要经过简单的以下几步:
- 调用
SqlSessionFactoryBuilder
对象的build(inputStream)
方法;SqlSessionFactoryBuilder
会根据输入流inputStream
等信息创建XMLConfigBuilder
对象;SqlSessionFactoryBuilder
调用XMLConfigBuilder
对象的parse()
方法;XMLConfigBuilder
对象返回Configuration
对象;SqlSessionFactoryBuilder
根据Configuration
对象创建一个DefaultSessionFactory
对象;SqlSessionFactoryBuilder
返回DefaultSessionFactory
对象给Client
,供Client
使用。
SqlSessionFactoryBuilder
相关的代码如下所示:
1
|
public SqlSessionFactory build(InputStream inputStream) {
|
上述的初始化过程中,涉及到了以下几个对象:
SqlSessionFactoryBuilder :
SqlSessionFactory
的构造器,用于创建SqlSessionFactory
,采用了Builder
设计模式Configuration :该对象是
mybatis-config.xml
文件中所有mybatis
配置信息SqlSessionFactory:
SqlSession
工厂类,以工厂形式创建SqlSession
对象,采用了Factory
工厂设计模式XmlConfigParser :负责将
mybatis-config.xml
配置文件解析成Configuration
对象,供SqlSessonFactoryBuilder
使用,创建SqlSessionFactory
2. 创建Configuration
对象的过程:
接着上述的MyBatis
初始化基本过程讨论,当SqlSessionFactoryBuilder
执行build()
方法,调用了XMLConfigBuilder
的parse()
方法,然后返回了Configuration
对象。那么parse()
方法是如何处理XML
文件,生成Configuration
对象的呢?
(1)XMLConfigBuilder
会将XML
配置文件的信息转换为Document
对象,而XML
配置定义文件DTD
转换成XMLMapperEntityResolver
对象,然后将二者封装到XpathParser
对象中,XpathParser
的作用是提供根据Xpath
表达式获取基本的DOM
节点Node
信息的操作。如下图所示:
(2)之后XMLConfigBuilder
调用parse()
方法:会从XPathParser
中取出<configuration>
节点对应的Node
对象,然后解析此Node
节点的子Node
:properties
, settings
, typeAliases
, typeHandlers
, objectFactory
, objectWrapperFactory
, plugins
, environments
, databaseIdProvider
, mappers
:
1
|
public Configuration parse() {
|
注意:在上述代码中,还有一个非常重要的地方,就是解析XML
配置文件子节点<mappers>
的方法mapperElements(root.evalNode("mappers"))
, 它将解析我们配置的Mapper.xml
配置文件,Mapper
配置文件可以说是MyBatis
的核心,MyBatis
的特性和理念都体现在此Mapper的配置和设计上。
(3)然后将这些值解析出来设置到Configuration
对象中:
解析子节点的过程这里就不一一介绍了,用户可以参照MyBatis
源码仔细揣摩,我们就看上述的environmentsElement(root.evalNode("environments"));
方法是如何将environments
的信息解析出来,设置到Configuration
对象中的:
1
|
/**
|
(4)返回Configuration
对象:
7.3 基于Java API
手动加载XML
配置文件创建Configuration
对象,并使用SqlSessionFactory
对象
我们可以使用XMLConfigBuilder
手动解析XML
配置文件来创建Configuration
对象,代码如下:
1
|
String resource = "mybatis-config.xml";
|
7.4 涉及到的设计模式
初始化的过程涉及到创建各种对象,所以会使用一些创建型的设计模式。在初始化的过程中,Builder
模式运用的比较多。
7.4.1 Builder模式应用1: SqlSessionFactory
的创建
对于创建SqlSessionFactory
时,会根据情况提供不同的参数,其参数组合可以有以下几种:
由于构造时参数不定,可以为其创建一个构造器Builder
,将SqlSessionFactory
的构建过程和表示分开:
7.4.2 Builder模式应用2: 数据库连接环境Environment
对象的创建
在构建Configuration
对象的过程中,XMLConfigParser
解析Mybatis
XML
配置文件节点<environment>
节点时,会有以下相应的代码:
1
|
private void environmentsElement(XNode context) throws Exception {
|
在Environment内部,定义了静态内部Builder
类:
1
|
public final class Environment {
|
原文作者:文/陶邦仁(简书作者)
原文标题:终结篇:MyBatis原理深入解析(一)