一个典型的配置
<?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"></properties>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</dataSource>
</environment>
</environments>
</configuration>
<configuration>
<properties resource="properties/db.properties"/>
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="useGeneratedKeys" value="true"/>
</settings>
<typeAliases>
<typeAlias alias="AdminMap" type="pojo.AdminMap"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driveClass}"/>
<property name="url" value="${url}"/>
<property name="username" value="${userName}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<!--加载映射文件-->
<mappers>
<mapper resource="mapper/adminMap.xml"></mapper>
<mapper resource="mapper/mapper.xml"></mapper>
</mappers>
</configuration>
典型的XMLConfigBuilder
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
loadCustomLogImpl(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);
}
}
evalNode最终使用的是xpath的evalNode. xpath有一套语法, 当然现在用的是最简单的. 就是查找所有的mappers/typeHandlers... 节点而已
最重要的 mapper parse 过程:
public void parse() {
if (!configuration.isResourceLoaded(resource)) {
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);
bindMapperForNamespace();
}
parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}
由于ResultMap支持extends,支持ER关联
<resultMap type= "Blog" id="blogResultMap" autoMapping ="true">
<id property="id" column="id" />
<result property="authorid" column="authorid" />
<!-- manyToOne -->
<association property ="author" column="authorid" javaType= "Author">
<id property ="id" column="bid" />
<!-- 或者使用 <id property ="id" column="authorid" /> -->
<result property ="name" column="bname" />
<result property ="age" column="age" />
</association>
</resultMap>
可以看到, 解析resultMap的过程,对于无extend的, 直接build即可, 对于有extend的则需要将extends的resultMap也加入resultMap列表.
同时有几个特殊的处理, resultMap已经包含了构造器, 最终映像里的继承的resultMap里面的构造器会剔除掉.
resultMap已经包含的映射, 同时在继承的resultMap也存在的, 也做了去重工作.
public ResultMap addResultMap(
String id,
Class<?> type,
String extend,
Discriminator discriminator,
List<ResultMapping> resultMappings,
Boolean autoMapping) {
id = applyCurrentNamespace(id, false);
extend = applyCurrentNamespace(extend, true);
if (extend != null) {
if (!configuration.hasResultMap(extend)) {
throw new IncompleteElementException("Could not find a parent resultmap with id '" + extend + "'");
}
ResultMap resultMap = configuration.getResultMap(extend);
List<ResultMapping> extendedResultMappings = new ArrayList<>(resultMap.getResultMappings());
extendedResultMappings.removeAll(resultMappings);
// Remove parent constructor if this resultMap declares a constructor.
boolean declaresConstructor = false;
for (ResultMapping resultMapping : resultMappings) {
if (resultMapping.getFlags().contains(ResultFlag.CONSTRUCTOR)) {
declaresConstructor = true;
break;
}
}
if (declaresConstructor) {
extendedResultMappings.removeIf(resultMapping -> resultMapping.getFlags().contains(ResultFlag.CONSTRUCTOR));
}
resultMappings.addAll(extendedResultMappings);
}
ResultMap resultMap = new ResultMap.Builder(configuration, id, type, resultMappings, autoMapping)
.discriminator(discriminator)
.build();
configuration.addResultMap(resultMap);
return resultMap;
}
以下是一个resultMapping, 可以看到我们经常见到的property,column,jdbcType,javaType, 以及部分用的不太多的关联查询字段.
public class ResultMapping {
private Configuration configuration;
private String property;
private String column;
private Class<?> javaType;
private JdbcType jdbcType;
// 类型转换handler
private TypeHandler<?> typeHandler;
private String nestedResultMapId;
private String nestedQueryId;
// 对应于配置 ex:notNullColumn="id"
private Set<String> notNullColumns;
private String columnPrefix;
//处理后的标记, 有两种:id和constructor, 也就是properties注入的两个办法, setter and 构造
private List<ResultFlag> flags;
private List<ResultMapping> composites;
private String resultSet;
private String foreignColumn;
//是否延迟加载, 对应节点的 fetchType 属性
private boolean lazy;