在前面的探索中,我们已经知道了 MyBatis 是如何 getMapper 并执行 Mapper 接口中的方法来进行数据库操作的。那么今天我们就来看看 Mapper 方法执行的“前因”:获取语句+参数映射。
我们还是按照之前的方式,使用 debug 在入口代码上打断点,步入源码。入口代码为:
List<Author> list = mapper.selectByName("Sylvia");
<select id="selectByName" resultMap="AuthorMap" >
select
id, name, sex, phone
from author
where name = #{
name}
</select>
从 Mapper XML 读取 SQL
首先,我们要思考一下,因为 SQL 语句是定义在 Mapper XML 中的,那么毫无疑问它会去读取该 Mapper XML 中的内容。可是它会在什么时候读取呢?答案是在获取 SqlSessionFactory 的时候,即执行
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
为了验证,我们可以在这行打个断点来一探究竟。
我们一直往下走,直到 org.apache.ibatis.builder.xml.XMLConfigBuilder 的 parseConfiguration 方法,我们在之前已经见过这个方法了,它的作用是解析 mybatis-config.xml 文件中的配置,将相应的元素内容转换到 Configuration 等类中:
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);
}
}
我们要看的是 Mapper XML 中 SQL 是不是在这个地方读取的,理所应当进入最后一行解析代码:mapperElement(root.evalNode(“mappers”));。这个时候我们会进入到 org.apache.ibatis.builder.xml.XMLConfigBuilder 的 mapperElement 方法:
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);
} else {
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
if (resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
InputStream inputStream = Resources.getResourceAsStream(resource);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();
}//略...
}
}
}
}
在这个方法中我们可以看到它在解析 mybatis-config.xml 中的 mappers 元素内容。注意最后一行代码:mapperParser.parse(); 看名字它似乎就是想要去解析 Mapper XML 的内容,进入 org.apache.ibatis.builder.xml.XMLMapperBuilder 的 parse() 方法:
public void parse() {
if (