人非要经历一番不同平时的劫难才能脱胎换骨,成为真正能解决问题的人
简介初始化过程1.解析XML配置文件1.1 Config文件的解析1.2 Mapper文件的解析1.2.1 解析CURD模板1.2.2 绑定Mapper到命名空间2.创建SqlSessionFactory总结
简介
首先我们再回顾下Mybaits的基本使用。
//加载配置文件
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//SqlSession 的获取
SqlSession sqlSession = sqlSessionFactory.openSession();
try{
//执行sql
User user = sqlSession.selectOne("MyMapper.selectUser", 1);//(SQL通过命名空间+SQLID 的格式定位)
}finally {
sqlSession .close();
}
一切都从SqlSessionFactoryBuilder说起。SqlSessionFactoryBuilder是通过builder设计模式来创建一个SqlSessionFactory 工厂。
创建SqlSessionFactory 最主要分为两步,
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
try {
// 1.xml配置文件解析器。
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
Configuration config = parser.parse()
// 2.根据解析到的配置,创建DefaultSqlSessionFactory
return build(config);
}
//创建默认的sqlsesion工厂。
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
初始化过程
1.解析XML配置文件
SqlSessionFactoryBuilder的第一步就是配置的解析。
配置文件主要分为两种:
- Conifg文件: 包括数据连接配置,全局设置配置。
- Mapper文件:用于SQL的统一管理,对SQL的配置。

1.1 Config文件的解析
Config文件的解析是由XMLConfigBuilder做的。
我们来看看parse()方法
public Configuration parse() {
parsed = true;
//从根节点开始解析
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
private void parseConfiguration(XNode root) {
try {
// 解析 properties配置
propertiesElement(root.evalNode("properties"));
// 解析 settings配置
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
//解析environment 配置
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
// 解析 mapper 配置
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
XMLConfigBuilder 根据约定,解析config 配置文件中的各个标签。并将相关配置信息放到Configuration 对象中返回。
列如:
Properties settings = settingsAsProperties(root.evalNode("settings"));//解析settings标签
settingsElement(settings);//设置settings配置到configuration 对象。
1.2 Mapper文件的解析
在解析Config配置文件过程中,会伴随Mapper.xml文件的解析。这个解析的工作是由XMLMapperBuilder 完成的。
config中的mapper文件位置配置
<mappers>
<mapper resource="mappers/UserMapper.xml"/>
</mappers>
Mapper.xml
<mapper namespace="com.wqd.dao.UserMapper">
<select id="selectUser" resultType="com.wqd.model.User">
select * from user where id= #{id}
</select>
</mapper>
XMLMapperBuilder
public void parse() {
if (!configuration.isResourceLoaded(resource)) {
//1.从根标签mapper开始解析mapper文件
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);
//2.绑定mapper到命名空间
bindMapperForNamespace();
}
parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}
//1.解析mapper文件
private void configurationElement(XNode context) {
try {
//命名空间必须要有
String namespace = context.getStringAttribute("namespace");
if (namespace == null || namespace.equals("")) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
builderAssistant.setCurrentNamespace(namespace);
//缓存的相关设置
cacheRefElement(context.evalNode("cache-ref"));
cacheElement(context.evalNode("cache"));
//参数类型解析
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
//结果集类型解析
resultMapElements(context.evalNodes("/mapper/resultMap"));
//解析sql标签配置的sql 片段
sqlElement(context.evalNodes("/mapper/sql"));
//解析select|insert|update|delete 标签配置的sql 模板(重点)
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
}
}
//2.绑定命名空间
private void bindMapperForNamespace() {
String namespace = builderAssistant.getCurrentNamespace();
if (namespace != null) {
Class<?> boundType = null;
try {
boundType = Resources.classForName(namespace);
} catch (ClassNotFoundException e) {
}
if (boundType != null) {//如果有对应的Class
if (!configuration.hasMapper(boundType)) {
configuration.addLoadedResource("namespace:" + namespace);
configuration.addMapper(boundType);//添加到缓存
}
}
}
}
这里我们重点讲讲XMLMapperBuilder 中有两个重要的点
1.2.1 解析CURD模板
也就是解析select|insert|update|delete
代表的SQL 模板。这四种标签配置的SQL模板是我们操作数据库时的SQL执行语句的模板。 我们可以理解为:一个select 标签表示一类动态SQL。
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
每一个select|insert|update|delete
由XMLStatementBuilder 解析
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
for (XNode context : list) {
//解析"select|insert|update|delete"标签
final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
try {
statementParser.parseStatementNode();
} catch (IncompleteElementException e) {
configuration.addIncompleteStatement(statementParser);
}
}
}