MyBatis 3 解析 Mapper 映射配置
前文《MyBatis 3 解析mybatis-config.xml配置》介绍了MyBatis 的行如何配置,我们现在就要来定义 SQL 映射语句了。 但首先,我们需要告诉 MyBatis 到哪里去找到这些语句。 在自动查找资源方面,Java 并没有提供一个很好的解决方案,所以最好的办法是直接告诉 MyBatis 到哪里去找映射文件。 你可以使用相对于类路径的资源引用,或完全限定资源定位符(包括 file:/// 形式的 URL),或类名和包名等。例如:
<!-- 使用相对于类路径的资源引用 --><mappers> <mapper resource="org/mybatis/builder/AuthorMapper.xml"/> <mapper resource="org/mybatis/builder/BlogMapper.xml"/> <mapper resource="org/mybatis/builder/PostMapper.xml"/></mappers>
<!-- 使用完全限定资源定位符(URL) --><mappers> <mapper url="file:///var/mappers/AuthorMapper.xml"/> <mapper url="file:///var/mappers/BlogMapper.xml"/> <mapper url="file:///var/mappers/PostMapper.xml"/></mappers>
<!-- 使用映射器接口实现类的完全限定类名 --><mappers> <mapper class="org.mybatis.builder.AuthorMapper"/> <mapper class="org.mybatis.builder.BlogMapper"/> <mapper class="org.mybatis.builder.PostMapper"/></mappers>
<!-- 将包内的映射器接口实现全部注册为映射器 --><mappers> <package name="org.mybatis.builder"/></mappers>
这些配置会告诉 MyBatis 去哪里找映射文件,XMLConfigBuilder.mapperElement()方法负责解析<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 //注意在for循环里每个mapper都重新new一个XMLMapperBuilder,来解析 XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments()); mapperParser.parse(); } else if (resource == null && url != null && mapperClass == null) { //使用绝对url路径 ErrorContext.instance().resource(url); InputStream inputStream = Resources.getUrlAsStream(url); //映射器比较复杂,调用XMLMapperBuilder XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments()); mapperParser.parse(); } else if (resource == null && url == null && mapperClass != null) { //使用java类名 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."); } } } } }
在确定了映射配置的位置后,交由XMLMapperBuilder.parse()方法解析映射配置
public void parse() { //protected final Set<String> loadedResources = new HashSet<String>(); //如果没有加载过再加载,防止重复加载 if (!configuration.isResourceLoaded(resource)) { //配置mapper configurationElement(parser.evalNode("/mapper")); //标记一下,将resource加入到loadedResources中 configuration.addLoadedResource(resource); //注册Mapper接口 bindMapperForNamespace(); } //以下是处理configurationElement方法中处理失败的节点 parsePendingResultMaps(); parsePendingChacheRefs(); parsePendingStatements(); }
// <mapper namespace="org.mybatis.example.BlogMapper">// <select id="selectBlog" parameterType="int" resultType="Blog">// select * from Blog where id = #{id}// </select>// </mapper>private void configurationElement(XNode context) { try { //1.配置namespace String namespace = context.getStringAttribute("namespace"); if (namespace.equals("")) { throw new BuilderException("Mapper's namespace cannot be empty"); } builderAssistant.setCurrentNamespace(namespace); //2.配置cache-ref cacheRefElement(context.evalNode("cache-ref")); //3.配置cache cacheElement(context.evalNode("cache")); //4.配置parameterMap(已经废弃,老式风格的参数映射) parameterMapElement(context.evalNodes("/mapper/parameterMap")); //5.配置resultMap(高级功能) resultMapElements(context.evalNodes("/mapper/resultMap")); //6.配置sql(定义可重用的 SQL 代码段) sqlElement(context.evalNodes("/mapper/sql")); //7.配置select|insert|update|delete TODO buildStatementFromContext(context.evalNodes("select|insert|update|delete")); } catch (Exception e) { throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e); } }
SQL 映射文件只有很少的几个顶级元素(按照应被定义的顺序列出):
cache – 该命名空间的缓存配置。
cache-ref – 引用其它命名空间的缓存配置。
resultMap – 描述如何从数据库结果集中加载对象,是最复杂也是最强大的元素。
parameterMap – 老式风格的参数映射。此元素已被废弃,并可能在将来被移除!请使用行内参数映射。文档中不会介绍此元素。
sql – 可被其它语句引用的可重用语句块。
insert – 映射插入语句。
update – 映射更新语句。
delete – 映射删除语句。
select – 映射查询语句。
private void bindMapperForNamespace() { String namespace = builderAssistant.getCurrentNamespace(); if (namespace != null) { Class<?> boundType = null; try { boundType = Resources.classForName(namespace); } catch (ClassNotFoundException e) { //ignore, bound type is not required } if (boundType != null) { if (!configuration.hasMapper(boundType)) { // Spring may not know the real resource name so we set a flag // to prevent loading again this resource from the mapper interface // look at MapperAnnotationBuilder#loadXmlResource configuration.addLoadedResource("namespace:" + namespace); configuration.addMapper(boundType); } } } }
到这里MyBatis解析映射配置的概要流程就差不多了,后面会继续介绍MyBatis解析映射配置的各个节点。
版权声明: 本文为 InfoQ 作者【杨家昌】的原创文章。
原文链接:【http://xie.infoq.cn/article/578b59a3118fe52642175495c】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
杨家昌
这是一位神秘人物 2020.05.06 加入
10年java老兵带新人通关,感兴趣的赶紧上车。
评论