Mybatis源码解析之Mapper映射器
Mapper是XML解析的最后子节点,也是最重要的一节。我们先了解下Mapper.xml文件结构:
SQL 映射文件有很少的几个顶级元素(按照它们应该被定义的顺序):
- cache – 给定命名空间的缓存配置。
- cache-ref – 其他命名空间缓存配置的引用。
- resultMap – 是最复杂也是最强大的元素,用来描述如何从数据库结果集中来加载对象。
parameterMap– 已废弃!老式风格的参数映射。内联参数是首选,这个元素可能在将来被移除,这里不会记录。- sql – 可被其他语句引用的可重用语句块。
- insert – 映射插入语句
- update – 映射更新语句
- delete – 映射删除语句
- select – 映射查询语句
解析mappers节点代码:
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(); } else if (resource == null && url != null && mapperClass == null) { ErrorContext.instance().resource(url); InputStream inputStream = Resources.getUrlAsStream(url); XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments()); mapperParser.parse(); } else if (resource == null && url == null && mapperClass != null) { Class<?> mapperInterface = Resources.classForName(mapperClass); configuration.addMapper(mapperInterface); } else { throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one."); } } } } }
从源码可以看到mapper配置有四种方案,下面是官方文档提供的例子:
<!-- Using classpath relative resources --> <mappers> <mapper resource="org/mybatis/builder/AuthorMapper.xml"/> <mapper resource="org/mybatis/builder/BlogMapper.xml"/> <mapper resource="org/mybatis/builder/PostMapper.xml"/> </mappers>
<!-- Using url fully qualified paths --> <mappers> <mapper url="file:///var/mappers/AuthorMapper.xml"/> <mapper url="file:///var/mappers/BlogMapper.xml"/> <mapper url="file:///var/mappers/PostMapper.xml"/> </mappers>
<!-- Using mapper interface classes --> <mappers> <mapper class="org.mybatis.builder.AuthorMapper"/> <mapper class="org.mybatis.builder.BlogMapper"/> <mapper class="org.mybatis.builder.PostMapper"/> </mappers>
<!-- Register all interfaces in a package as mappers --> <mappers> <package name="org.mybatis.builder"/> </mappers>
这里我们使用resource讲解,可以看到mappers子节点mepper配置 resourse,url,mepperClass每次只能配置一种,package子节点是通过包扫描器进行扫描。虽然是通过不同的方式进行解析,但最终的处理类都是XMLMapperBuilder 解析器的parse方法:
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(); }
看下这个方法:
public void parse() { //判断configuration对象是否已经加载对应的mapper.xml文件,避免重复加载 if (!configuration.isResourceLoaded(resource)) { //解析mapper.xml文件 configurationElement(parser.evalNode("/mapper")); //加载mapper.xml文件,其实添加到Set集合 configuration.addLoadedResource(resource); //绑定namespace与mapper.xml关系 bindMapperForNamespace(); } //这三个方法没看懂,后面看懂了再补上 parsePendingResultMaps(); parsePendingCacheRefs(); parsePendingStatements(); }
configurationElement方法解析了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"); } //设置当前namespace builderAssistant.setCurrentNamespace(namespace); //解析缓存引用 cacheRefElement(context.evalNode("cache-ref")); //解析缓存 cacheElement(context.evalNode("cache")); //处理参数映射配置 parameterMapElement(context.evalNodes("/mapper/parameterMap")); //处理结果映射配置 resultMapElements(context.evalNodes("/mapper/resultMap")); //处理sql片段 sqlElement(context.evalNodes("/mapper/sql")); //使用XMLStatementBuilder的对象解析mapper的<select>、<insert>、<update>、<delete>节点, //mybaits会使用MappedStatement.Builder类build一个MappedStatement对象, //所以mybaits中一个sql对应一个MappedStatement buildStatementFromContext(context.evalNodes("select|insert|update|delete")); } catch (Exception e) { throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e); } }

浙公网安备 33010602011771号