配置文件
Mybatis的初始化主要是对配置文件的解析,主要为Mybaits-config.xml和*Mapper.xml的解析
建造者模式
Mybatis使用建造者模式来构建解析配置文件
BaseBuilder
抽象类,起着建造者接口的作用,其子类为具体的建造者,负责不同的解析任务
XMLConfigBuilder
负责解析Mybaits-config.xml文件
parse()是核心方法,其返回了一个Configuration对象,该对象包含了Mybatis的所有配置内容
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
propertiesElement(root.evalNode("properties"));
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);
// read it after objectFactory and objectWrapperFactory issue #631
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
从方法中可以看到,主要对配置文件的各个节点进行解析并将配置内容加到全局配置对象Configuration中
XMLMapperBuilder
负责解析xml映射文件
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"));
sqlElement(context.evalNodes("/mapper/sql"));
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
}
}
如代码所示,会对映射文件的namespace、cache、cache-ref、parameterMap、resultMap、sql以及语句节点进行解析。
cache
用在二级缓存中,使用MapperBuilderAssistant.useNewCache()创建Cache对象,并将其添加到Configuration.caches(StrictMap类型,插入重复key抛异常)中保存,key为cache的id(默认映射文件的namespace)
cache-ref
如果希望多个namespace共用一个二级缓存,即共用同一个cacha对象,使用该节点。
<cache-ref namespace="namespace1">
代表共用namespace1的cache对象
ResultMap
ResultMap节点对应着ResultMap对象,resultMap中的每个节点对应着一个ResultMapping对象。
ResultMap的属性如下:
private Configuration configuration;
//resultMap的id
private String id;
//resultMap对应的Type
private Class<?> type;
//所有的resultMappings对象集合
private List<ResultMapping> resultMappings;
//带有id标志的映射关系,如<id>和<idArg>
private List<ResultMapping> idResultMappings;
//<constructor>标志的所有映射关系,如<constructor>节点下的所有子元素
private List<ResultMapping> constructorResultMappings;
//除了<constructor>标志之外的所有映射关系
private List<ResultMapping> propertyResultMappings;
//映射关系中涉及的column名字
private Set<String> mappedColumns;
//映射关系中涉及的所有属性名字
private Set<String> mappedProperties;
//鉴别器
private Discriminator discriminator;
//是否有嵌套结果映射
private boolean hasNestedResultMaps;
//是否有嵌套查询
private boolean hasNestedQueries;
//是否开启自动映射
private Boolean autoMapping;
ResultMapping的属性如下:
private Configuration configuration;
// 列名
private String property;
// 属性名
private String column;
private Class<?> javaType;
private JdbcType jdbcType;
private TypeHandler<?> typeHandler;
private String nestedResultMapId;
private String nestedQueryId;
private Set<String> notNullColumns;
private String columnPrefix;
private List<ResultFlag> flags;
private List<ResultMapping> composites;
private String resultSet;
private String foreignColumn;
private boolean lazy;
大多见名知意
<result id="BaseResult" type = "cn.jwb5.test.User">
<id column = "id" property = "id"/>
<id column = "user_name" property = "userName"/>
<id column = "pwd" property = "pwd"/>
<id column = "email" property = "email"/>
</result>
对应图如图所示
resultMap对象构建后会添加到Configuration.resultMaps集合中,其他类似<constructor>、<assocation>、<collection>
等节点其实可以看作是嵌套的ResultMap
XMLStatementBuilder
用于解析SQL节点,主要包括对<include> 和 <selectKey>
的解析,其余的动态标签交给SqlNode和SqlSource解析
include
将<include>
节点替换为<sql>
节点中对应的sql片段
<sql id="somesql">
from ${tablename}
</sql>
<select id="find" resultType="int">
select * from FROM
<include refid="somesql">
<property name="somesql" value="user"/>
</include>
</select>
时序图如图所示
selectKey
parseSelectKeyNode()
中会通过XMLLanguageDriver.createSqlSource()
创建对应的SqlSource
对象,其内部使用XMLScriptBuilder.parseScriptNode()
来创建对应的SqlSource
对象来解析,具体解析方式请看前面讲过的SqlSource&SqlNode