上篇文章 Mybatis系列(八)databaseIdProvider及plugin简单介绍,我们对mybatis的数据库厂商标识和插件有了简单的认识,这一节我们来看mybatis配置文件最重要的一个节点mappers(映射器)。
一、mappers的配置——引入映射器的四种方法
1.使用相对于类路径的资源,通过resource引入映射器
<mappers>
<mapper resource="com/sean/mapper/UserMapper.xml"/>
</mappers>
2.用包名,通过package引入映射器
<mappers>
<package name ="com.sean.mapper"/>
</mappers>
3.用类的全限定名注册,通过class引入映射器
<mappers>
<mapper class="com.sean.mapper.UserMapper"/>
</mappers>
4.使用完全限定资源定位符(URL)
<mappers>
<mapper url="file:///git2/mybatisDemo/src/main/resources/com/sean/mapper/UserMapper.xml"/>
</mappers>
以上四种引入注册器的方式大家应该都清楚,平时开发中常用的也是通过resource引入注册器,但是笔者mybatisDemo项目通过package,class引入却一直报错。如果有遇到和笔者一样问题的,请往下看
报错如下:
没有找到有效的绑定。
使用这种方式必须保证 接口与mapper文件同名(不区分大小写)
,我这里贴一下我之前的项目结构
可以看到配置文件的xml文件路径并没有和mapper接口对应,所以我测试的一会一直绑定不到mapper映射文件找不到指定的方法,于是我改成了这样的项目结构
再通过<package name ="com.sean.mapper"/>
就可以完成mapper的注入了。如何配置掌握了,那我们就来看mybatis是如何解析mappers节点的。
二、mappers源码解析
还是从xml解析mappers节点看起,XMLConfigBuilder类下的this.mapperElement(root.evalNode("mappers"));
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
Iterator i$ = parent.getChildren().iterator();
while(true) {
while(i$.hasNext()) {
XNode child = (XNode)i$.next();
String resource;
//如果mappers节点的子节点是package, 那么就扫描package下的那么属性, 注入进configuration
if ("package".equals(child.getName())) {
resource = child.getStringAttribute("name");
this.configuration.addMappers(resource);
} else {
//解析resource, url, class属性值
resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
XMLMapperBuilder mapperParser;
InputStream inputStream;
//mappers节点只能指定resource, url, class中的一个,不能指定多个
if (resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
inputStream = Resources.getResourceAsStream(resource);
mapperParser = new XMLMapperBuilder(inputStream, this.configuration, resource, this.configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {
ErrorContext.instance().resource(url);
inputStream = Resources.getUrlAsStream(url);
mapperParser = new XMLMapperBuilder(inputStream, this.configuration, url, this.configuration.getSqlFragments());
mapperParser.parse();
} else {
if (resource != null || url != null || mapperClass == null) {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
Class<?> mapperInterface = Resources.classForName(mapperClass);
this.configuration.addMapper(mapperInterface);
}
}
}
return;
}
}
}
查看源码,无论配置是哪种注入方式,最终的处理类都是XMLMapperBuilder 解析器的parse方法
public void parse() {
//判断configuration对象是否已经加载对应的mapper.xml文件,避免重复加载
if (!this.configuration.isResourceLoaded(this.resource)) {
//解析mapper.xml文件
this.configurationElement(this.parser.evalNode("/mapper"));
//加载mapper.xml文件,其实添加到Set集合
this.configuration.addLoadedResource(this.resource);
//绑定namespace与mapper.xml关系
this.bindMapperForNamespace();
}
this.parsePendingResultMaps();
this.parsePendingChacheRefs();
this.parsePendingStatements();
}
至此,整个mybatis配置文件中的mappers
节点与具体mapper.xml
中mapper
映射文件中指定的namespace
绑定完成。后续便是mapper映射文件是如何解析我们写的SQL(select,insert,update,delete)以及如何指定参数类型和查询结果返回类型的,我们下节继续来看吧。