1. 準(zhǔn)備工作
為了看清楚MyBatis的整個(gè)初始化過(guò)程,先創(chuàng)建一個(gè)簡(jiǎn)單的Java項(xiàng)目,目錄結(jié)構(gòu)如下圖所示:

1.1 Product 產(chǎn)品實(shí)體類
public class Product {
private long id;
private String productName;
private String productContent;
private String price;
private int sort;
private int falseSales;
private long category_id;
private byte type;
private byte state;
// PS:省略setter、getter函數(shù)
}
1.2 ProductMapper 產(chǎn)品持久化接口
public interface ProductMapper {
/**
* 查詢所有的產(chǎn)品
* @return
*/
List<Product> selectProductList();
}
1.3 ProductMapper.xml 產(chǎn)品映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="team.njupt.mapper.ProductMapper">
<select id="selectProductList" resultType="team.njupt.entity.Product">
select * from product
</select>
</mapper>
1.4 db.properties 數(shù)據(jù)庫(kù)配置文件
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/waimai?useUnicode=true&characterEncoding=utf8
username=root
password=xxxxxx
1.5 mybatis.xml MyBatis的配置文件
<?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">
<!--<property name="username" value="dev_user"/>-->
<!--<property name="password" value="F2Fa3!33TYyg"/>-->
</properties>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="team/njupt/mapper/ProductMapper.xml"/>
</mappers>
</configuration>
1.6 Main 主函數(shù)
public class Main {
public static void main(String[] args) throws IOException {
String resource = "mybatis.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
ProductMapper productMapper = sqlSession.getMapper(ProductMapper.class);
List<Product> productList = productMapper.selectProductList();
for (Product product : productList) {
System.out.printf(product.toString());
}
} finally {
sqlSession.close();
}
}
}
2. MyBatis初始化過(guò)程
2.1 獲取配置文件
當(dāng)系統(tǒng)初始化時(shí),首先會(huì)讀取配置文件,并將其解析成InputStream
String resource = "mybatis.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
2.2 創(chuàng)建SqlSessionFactoryBuilder對(duì)象
從SqlSessionFactoryBuilder的名字中可以看出,SqlSessionFactoryBuilder是用來(lái)創(chuàng)建SqlSessionFactory對(duì)象的。
來(lái)看一下SqlSessionFactoryBuilder源碼:

SqlSessionFactoryBuilder中只有一些重載的build函數(shù),這些build函數(shù)的入?yún)⒍际荕yBatis配置文件的輸入流,返回值都是SqlSessionFactory;由此可見,SqlSessionFactoryBuilder的作用很純粹,就是用來(lái)通過(guò)配置文件創(chuàng)建SqlSessionFactory對(duì)象的。
2.3 SqlSessionFactory創(chuàng)建過(guò)程
下面具體來(lái)看一下,build函數(shù)是如何創(chuàng)建SqlSessionFactory對(duì)象的。
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.
}
}
}
2.3.1 構(gòu)造XMLConfigBuilder對(duì)象
build函數(shù)首先會(huì)構(gòu)造一個(gè)XMLConfigBuilder對(duì)象,從名字上大致可以猜到,該對(duì)象是用來(lái)解析XML配置文件的。下面來(lái)看一下XMLConfigBuilder的體系結(jié)構(gòu)。

XMLxxxBuilder是用來(lái)解析XML配置文件的,不同類型XMLxxxBuilder用來(lái)解析MyBatis配置文件的不同部位。比如:XMLConfigBuilder用來(lái)解析MyBatis的配置文件,XMLMapperBuilder用來(lái)解析MyBatis中的映射文件(如上文提到的ProductMapper.xml),XMLStatementBuilder用來(lái)解析映射文件中的SQL語(yǔ)句。這些
XMLxxxBuilder都有一個(gè)共同的父類——BaseBuilder。這個(gè)父類維護(hù)了一個(gè)全局的Configuration對(duì)象,MyBatis的配置文件解析后就以Configuration對(duì)象的形式存儲(chǔ)。-
當(dāng)創(chuàng)建
XMLConfigBuilder對(duì)象時(shí),就會(huì)初始化Configuration對(duì)象,并且在初始化Configuration對(duì)象的時(shí)候,一些別名會(huì)被注冊(cè)到Configuration的typeAliasRegistry容器中。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; }public Configuration() { typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class); typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class); typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class); typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class); typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class); typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class); typeAliasRegistry.registerAlias("FIFO", FifoCache.class); typeAliasRegistry.registerAlias("LRU", LruCache.class); typeAliasRegistry.registerAlias("SOFT", SoftCache.class); typeAliasRegistry.registerAlias("WEAK", WeakCache.class); …… }
2.3.2 解析配置文件
當(dāng)有了XMLConfigBuilder對(duì)象之后,接下來(lái)就可以用它來(lái)解析配置文件了。
private void parseConfiguration(XNode root) {
try {
// 解析<properties>節(jié)點(diǎn)
propertiesElement(root.evalNode("properties"));
// 解析<settings>節(jié)點(diǎn)
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
// 解析<typeAliases>節(jié)點(diǎn)
typeAliasesElement(root.evalNode("typeAliases"));
// 解析<plugins>節(jié)點(diǎn)
pluginElement(root.evalNode("plugins"));
// 解析<objectFactory>節(jié)點(diǎn)
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
// 解析<reflectorFactory>節(jié)點(diǎn)
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
// 解析<environments>節(jié)點(diǎn)
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
// 解析<mappers>節(jié)點(diǎn)
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
從上述代碼中可以看到,XMLConfigBuilder會(huì)依次解析配置文件中的<properties>、< settings >、< environments>、< typeAliases >、< plugins >、< mappers >等屬性。下面介紹下幾個(gè)重要屬性的解析過(guò)程。
2.3.2.1 <properties>節(jié)點(diǎn)的解析過(guò)程
-
<properties>節(jié)點(diǎn)的定義如下:
<properties resource="org/mybatis/example/config.properties"> <property name="username" value="dev_user"/> <property name="password" value="F2Fa3!33TYyg"/> </properties> -
<properties>節(jié)點(diǎn)的解析過(guò)程:
/** * @Param context <properties>節(jié)點(diǎn) */ private void propertiesElement(XNode context) throws Exception { if (context != null) { // 獲取<properties>節(jié)點(diǎn)的所有子節(jié)點(diǎn) Properties defaults = context.getChildrenAsProperties(); // 獲取<properties>節(jié)點(diǎn)上的resource屬性 String resource = context.getStringAttribute("resource"); // 獲取<properties>節(jié)點(diǎn)上的url屬性 String url = context.getStringAttribute("url"); // resource和url不能同時(shí)存在 if (resource != null && url != null) { throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other."); } if (resource != null) { // 獲取resource屬性值對(duì)應(yīng)的properties文件中的鍵值對(duì),并添加至defaults容器中 defaults.putAll(Resources.getResourceAsProperties(resource)); } else if (url != null) { // 獲取url屬性值對(duì)應(yīng)的properties文件中的鍵值對(duì),并添加至defaults容器中 defaults.putAll(Resources.getUrlAsProperties(url)); } // 獲取configuration中原本的屬性,并添加至defaults容器中 Properties vars = configuration.getVariables(); if (vars != null) { defaults.putAll(vars); } parser.setVariables(defaults); // 將defaults容器添加至configuration中 configuration.setVariables(defaults); } }- 首先讀取
<resources>節(jié)點(diǎn)下的所有<resource>節(jié)點(diǎn),并將每個(gè)節(jié)點(diǎn)的name和value屬性存入Properties中。 - 然后讀取
<resources>節(jié)點(diǎn)上的resource、url屬性,并獲取指定配置文件中的name和value,也存入Properties中。(PS:由此可知,如果resource節(jié)點(diǎn)上定義的屬性和properties文件中的屬性重名,那么properties文件中的屬性值會(huì)覆蓋resource節(jié)點(diǎn)上定義的屬性值。) - 最終,攜帶所有屬性的
Properties對(duì)象會(huì)被存儲(chǔ)在Configuration對(duì)象中。
- 首先讀取
2.3.2.2 <settings>節(jié)點(diǎn)的解析過(guò)程
- <settings>節(jié)點(diǎn)的定義如下:
<settings> <setting name="cacheEnabled" value="true"/> <setting name="lazyLoadingEnabled" value="true"/> <setting name="multipleResultSetsEnabled" value="true"/> </settings> - <settings>節(jié)點(diǎn)的解析過(guò)程:
<settings>屬性的解析過(guò)程和<properties>屬性的解析過(guò)程極為類似,這里不再贅述。最終,所有的setting屬性都被存儲(chǔ)在Configuration對(duì)象中。
2.3.2.3 <typeAliases>屬性的解析過(guò)程
<typeAliases>屬性的定義方式有如下兩種:
- 方式1:
<typeAliases> <typeAlias alias="Author" type="domain.blog.Author"/> <typeAlias alias="Blog" type="domain.blog.Blog"/> </typeAliases> - 方式2:
采用這種方式時(shí),MyBatis會(huì)為指定包下的所有類起一個(gè)別名,該別名為首字母小寫的類名。<typeAliases> <package name="domain.blog"/> </typeAliases>
<typeAliases>節(jié)點(diǎn)的解析過(guò)程如下:
private void typeAliasesElement(XNode parent) {
if (parent != null) {
// 遍歷<typeAliases>下的所有子節(jié)點(diǎn)
for (XNode child : parent.getChildren()) {
// 若當(dāng)前結(jié)點(diǎn)為<package>
if ("package".equals(child.getName())) {
// 獲取<package>上的name屬性(包名)
String typeAliasPackage = child.getStringAttribute("name");
// 為該包下的所有類起個(gè)別名,并注冊(cè)進(jìn)configuration的typeAliasRegistry中
configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
}
// 如果當(dāng)前結(jié)點(diǎn)為< typeAlias >
else {
// 獲取alias和type屬性
String alias = child.getStringAttribute("alias");
String type = child.getStringAttribute("type");
// 注冊(cè)進(jìn)configuration的typeAliasRegistry中
try {
Class<?> clazz = Resources.classForName(type);
if (alias == null) {
typeAliasRegistry.registerAlias(clazz);
} else {
typeAliasRegistry.registerAlias(alias, clazz);
}
} catch (ClassNotFoundException e) {
throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
}
}
}
}
}
- 如果
<typeAliases>節(jié)點(diǎn)下定義了<package>節(jié)點(diǎn),那么MyBatis會(huì)給該包下的所有類起一個(gè)別名(以類名首字母小寫作為別名) - 如果
<typeAliases>節(jié)點(diǎn)下定義了<typeAlias>節(jié)點(diǎn),那么MyBatis就會(huì)給指定的類起指定的別名。 - 這些別名都會(huì)被存入
configuration的typeAliasRegistry容器中。
2.3.2.4 <mappers>節(jié)點(diǎn)的解析過(guò)程
<mappers>節(jié)點(diǎn)的定義方式有如下四種:
- 方式1:
<mappers>
<package name="org.mybatis.builder"/>
</mappers>
- 方式2:
<mappers>
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
</mappers>
- 方式3:
<mappers>
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
</mappers>
- 方式4:
<mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
</mappers>
<mappers>節(jié)點(diǎn)的解析過(guò)程如下:
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
// 遍歷<mappers>下所有子節(jié)點(diǎn)
for (XNode child : parent.getChildren()) {
// 如果當(dāng)前節(jié)點(diǎn)為<package>
if ("package".equals(child.getName())) {
// 獲取<package>的name屬性(該屬性值為mapper class所在的包名)
String mapperPackage = child.getStringAttribute("name");
// 將該包下的所有Mapper Class注冊(cè)到configuration的mapperRegistry容器中
configuration.addMappers(mapperPackage);
}
// 如果當(dāng)前節(jié)點(diǎn)為<mapper>
else {
// 依次獲取resource、url、class屬性
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
// 解析resource屬性(Mapper.xml文件的路徑)
if (resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
// 將Mapper.xml文件解析成輸入流
InputStream inputStream = Resources.getResourceAsStream(resource);
// 使用XMLMapperBuilder解析Mapper.xml,并將Mapper Class注冊(cè)進(jìn)configuration對(duì)象的mapperRegistry容器中
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();
}
// 解析url屬性(Mapper.xml文件的路徑)
else if (resource == null && url != null && mapperClass == null) {
ErrorContext.instance().resource(url);
InputStream inputStream = Resources.getUrlAsStream(url);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
mapperParser.parse();
}
// 解析class屬性(Mapper Class的全限定名)
else if (resource == null && url == null && mapperClass != null) {
// 將Mapper Class的權(quán)限定名轉(zhuǎn)化成Class對(duì)象
Class<?> mapperInterface = Resources.classForName(mapperClass);
// 注冊(cè)進(jìn)configuration對(duì)象的mapperRegistry容器中
configuration.addMapper(mapperInterface);
} else {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
}
}
}
}
- MyBatis會(huì)遍歷
<mappers>下所有的子節(jié)點(diǎn),如果當(dāng)前遍歷到的節(jié)點(diǎn)是<package>,則MyBatis會(huì)將該包下的所有Mapper Class注冊(cè)到configuration的mapperRegistry容器中。 - 如果當(dāng)前節(jié)點(diǎn)為
<mapper>,則會(huì)依次獲取resource、url、class屬性,解析映射文件,并將映射文件對(duì)應(yīng)的Mapper Class注冊(cè)到configuration的mapperRegistry容器中。
其中,<mapper>節(jié)點(diǎn)的解析過(guò)程如下:
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();
-
在解析前,首先需要?jiǎng)?chuàng)建
XMLMapperBuilder,創(chuàng)建過(guò)程如下:private XMLMapperBuilder(XPathParser parser, Configuration configuration, String resource, Map<String, XNode> sqlFragments) { // 將configuration賦給BaseBuilder super(configuration); // 創(chuàng)建MapperBuilderAssistant對(duì)象(該對(duì)象為MapperBuilder的協(xié)助者) this.builderAssistant = new MapperBuilderAssistant(configuration, resource); this.parser = parser; this.sqlFragments = sqlFragments; this.resource = resource; }- 首先會(huì)初始化父類
BaseBuilder,并將configuration賦給BaseBuilder; - 然后創(chuàng)建
MapperBuilderAssistant對(duì)象,該對(duì)象為XMLMapperBuilder的協(xié)助者,用來(lái)協(xié)助XMLMapperBuilder完成一些解析映射文件的動(dòng)作。
- 首先會(huì)初始化父類
-
當(dāng)有了
XMLMapperBuilder后,便可進(jìn)入解析<mapper>的過(guò)程:public void parse() { // 若當(dāng)前的Mapper.xml尚未被解析,則開始解析 // PS:若<mappers>節(jié)點(diǎn)下有相同的<mapper>節(jié)點(diǎn),那么就無(wú)需再次解析了 if (!configuration.isResourceLoaded(resource)) { // 解析<mapper>節(jié)點(diǎn) configurationElement(parser.evalNode("/mapper")); // 將該Mapper.xml添加至configuration的LoadedResource容器中,下回?zé)o需再解析 configuration.addLoadedResource(resource); // 將該Mapper.xml對(duì)應(yīng)的Mapper Class注冊(cè)進(jìn)configuration的mapperRegistry容器中 bindMapperForNamespace(); } parsePendingResultMaps(); parsePendingCacheRefs(); parsePendingStatements(); } -
configurationElement函數(shù)private void configurationElement(XNode context) { try { // 獲取<mapper>節(jié)點(diǎn)上的namespace屬性,該屬性必須存在,表示當(dāng)前映射文件對(duì)應(yīng)的Mapper Class是誰(shuí) String namespace = context.getStringAttribute("namespace"); if (namespace == null || namespace.equals("")) { throw new BuilderException("Mapper's namespace cannot be empty"); } // 將namespace屬性值賦給builderAssistant builderAssistant.setCurrentNamespace(namespace); // 解析<cache-ref>節(jié)點(diǎn) cacheRefElement(context.evalNode("cache-ref")); // 解析<cache>節(jié)點(diǎn) cacheElement(context.evalNode("cache")); // 解析<parameterMap>節(jié)點(diǎn) parameterMapElement(context.evalNodes("/mapper/parameterMap")); // 解析<resultMap>節(jié)點(diǎn) resultMapElements(context.evalNodes("/mapper/resultMap")); // 解析<sql>節(jié)點(diǎn) sqlElement(context.evalNodes("/mapper/sql")); // 解析sql語(yǔ)句 buildStatementFromContext(context.evalNodes("select|insert|update|delete")); } catch (Exception e) { throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e); } } -
resultMapElements函數(shù)
該函數(shù)用于解析映射文件中所有的<resultMap>節(jié)點(diǎn),這些節(jié)點(diǎn)會(huì)被解析成ResultMap對(duì)象,存儲(chǔ)在Configuration對(duì)象的resultMaps容器中。-
<resultMap>節(jié)點(diǎn)定義如下:
<resultMap id="userResultMap" type="User"> <constructor> <idArg column="id" javaType="int"/> <arg column="username" javaType="String"/> </constructor> <result property="username" column="user_name"/> <result property="password" column="hashed_password"/> </resultMap>-
<resultMap>節(jié)點(diǎn)的解析過(guò)程:
private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings) throws Exception { ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier()); // 獲取<ResultMap>上的id屬性 String id = resultMapNode.getStringAttribute("id", resultMapNode.getValueBasedIdentifier()); // 獲取<ResultMap>上的type屬性(即resultMap的返回值類型) String type = resultMapNode.getStringAttribute("type", resultMapNode.getStringAttribute("ofType", resultMapNode.getStringAttribute("resultType", resultMapNode.getStringAttribute("javaType")))); // 獲取extends屬性 String extend = resultMapNode.getStringAttribute("extends"); // 獲取autoMapping屬性 Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping"); // 將resultMap的返回值類型轉(zhuǎn)換成Class對(duì)象 Class<?> typeClass = resolveClass(type); Discriminator discriminator = null; // resultMappings用于存儲(chǔ)<resultMap>下所有的子節(jié)點(diǎn) List<ResultMapping> resultMappings = new ArrayList<ResultMapping>(); resultMappings.addAll(additionalResultMappings); // 獲取并遍歷<resultMap>下所有的子節(jié)點(diǎn) List<XNode> resultChildren = resultMapNode.getChildren(); for (XNode resultChild : resultChildren) { // 若當(dāng)前節(jié)點(diǎn)為<constructor>,則將它的子節(jié)點(diǎn)們添加到resultMappings中去 if ("constructor".equals(resultChild.getName())) { processConstructorElement(resultChild, typeClass, resultMappings); } // 若當(dāng)前節(jié)點(diǎn)為<discriminator>,則進(jìn)行條件判斷,并將命中的子節(jié)點(diǎn)添加到resultMappings中去 else if ("discriminator".equals(resultChild.getName())) { discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings); } // 若當(dāng)前節(jié)點(diǎn)為<result>、<association>、<collection>,則將其添加到resultMappings中去 else { // PS:flags僅用于區(qū)分當(dāng)前節(jié)點(diǎn)是否是<id>或<idArg>,因?yàn)檫@兩個(gè)節(jié)點(diǎn)的屬性名為name,而其他節(jié)點(diǎn)的屬性名為property List<ResultFlag> flags = new ArrayList<ResultFlag>(); if ("id".equals(resultChild.getName())) { flags.add(ResultFlag.ID); } resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags)); } } // ResultMapResolver的作用是生成ResultMap對(duì)象,并將其加入到Configuration對(duì)象的resultMaps容器中(具體過(guò)程見下) ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping); try { return resultMapResolver.resolve(); } catch (IncompleteElementException e) { configuration.addIncompleteResultMap(resultMapResolver); throw e; } }ResultMapResolver這個(gè)類很純粹,有且僅有一個(gè)函數(shù)resolve,用于構(gòu)造ResultMap對(duì)象,并將其存入Configuration對(duì)象的resultMaps容器中;而這個(gè)過(guò)程是借助于MapperBuilderAssistant.addResultMap完成的。public ResultMap resolve() { return assistant.addResultMap(this.id, this.type, this.extend, this.discriminator, this.resultMappings, this.autoMapping); } -
-
sqlElement函數(shù)
該函數(shù)用于解析映射文件中所有的<sql>節(jié)點(diǎn),并將這些節(jié)點(diǎn)存儲(chǔ)在當(dāng)前映射文件所對(duì)應(yīng)的XMLMapperBuilder對(duì)象的sqlFragments容器中,供解析sql語(yǔ)句時(shí)使用。<sql id="userColumns"> ${alias}.id,${alias}.username,${alias}.password </sql>
-
buildStatementFromContext函數(shù)
該函數(shù)會(huì)將映射文件中的sql語(yǔ)句解析成MappedStatement對(duì)象,并存在configuration的mappedStatements。
2.3.3 創(chuàng)建SqlSessionFactory對(duì)象
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.
}
}
}
回過(guò)頭來(lái)再看一下SqlSessionFactory的build函數(shù),剛才說(shuō)了半天,介紹了XMLConfigBuilder解析映射文件的過(guò)程,解析完成之后parser.parse()函數(shù)會(huì)返回一個(gè)包含了映射文件解析結(jié)果的configuration對(duì)象,緊接著,這個(gè)對(duì)象將作為參數(shù)傳遞給另一個(gè)build函數(shù),如下:
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
這個(gè)函數(shù)將configuration作為參數(shù),創(chuàng)建了DefaultSqlSessionFactory對(duì)象。
DefaultSqlSessionFactory是接口SqlSessionFactory的一個(gè)實(shí)現(xiàn)類,SqlSessionFactory的體系結(jié)構(gòu)如下圖所示:

此時(shí),SqlSessionFactory創(chuàng)建完畢!