mybatis-7-Mapper解析-mapper.xml

mac2022-06-30  25

上一篇文章完成了mybatis-config.xml中所有属性的解析,这里接着对<mapper>标签进行解析。

回顾:

标签配置xml如下:

<mappers> <mapper resource="org/apache/ibatis/builder/BlogMapper.xml"/> <mapper url="file:./src/test/java/org/apache/ibatis/builder/NestedBlogMapper.xml"/> <mapper class="org.apache.ibatis.builder.CachedAuthorMapper"/> <package name="org.apache.ibatis.builder.mapper"/> </mappers> //XMLConfigBuilder类 InputStream inputStream = Resources.getUrlAsStream(url); XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments()); mapperParser.parse();

XMLMapperBuilder 的parse()方法

public void parse() { if (!configuration.isResourceLoaded(resource)) { configurationElement(parser.evalNode("/mapper")); configuration.addLoadedResource(resource); bindMapperForNamespace(); } parsePendingResultMaps(); parsePendingCacheRefs(); parsePendingStatements(); }

mapper文件的格式如下

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="org.apache.ibatis.domain.blog.mappers.BlogMapper"> <!--<insert>、<select>、<resultMap>等--> </mapper>

 一、configurationElement  基本标签的解析

private void configurationElement(XNode context) { try { String namespace = context.getStringAttribute("namespace"); //如果namespace 为空则抛异常 builderAssistant.setCurrentNamespace(namespace); cacheRefElement(context.evalNode("cache-ref")); cacheElement(context.evalNode("cache")); parameterMapElement(context.evalNodes("/mapper/parameterMap")); resultMapElements(context.evalNodes("/mapper/resultMap")); sqlElement(context.evalNodes("/mapper/sql")); buildStatementFromContext(context.evalNodes("select|insert|update|delete")); } catch (Exception e) { throw new BuilderException... } }

1)<cache> 缓存解析

<cache readOnly="true"/> private void cacheElement(XNode context) throws Exception { if (context != null) { //获取标签的type属性,没指定就使用默认的PERPETUAL String type = context.getStringAttribute("type", "PERPETUAL"); Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type); //获取装饰策略,没指定的话默认为LRU,装饰器在前面已介绍 String eviction = context.getStringAttribute("eviction", "LRU"); Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction); Long flushInterval = context.getLongAttribute("flushInterval");//刷新间隔 Integer size = context.getIntAttribute("size");//缓存大小 boolean readWrite = !context.getBooleanAttribute("readOnly", false); boolean blocking = context.getBooleanAttribute("blocking", false);//是否阻塞 Properties props = context.getChildrenAsProperties();//获取所有的子属性 //调用辅助类,完成cache的创建 builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props); } }

MapperBuilderAssistant

public Cache useNewCache(Class<? extends Cache> typeClass, Class<? extends Cache> evictionClass, Long flushInterval, Integer size, boolean readWrite, boolean blocking, Properties props) { Cache cache = new CacheBuilder(currentNamespace) .implementation(valueOrDefault(typeClass, PerpetualCache.class)) .addDecorator(valueOrDefault(evictionClass, LruCache.class)) .clearInterval(flushInterval) .size(size) .readWrite(readWrite) .blocking(blocking) .properties(props) .build(); configuration.addCache(cache);//加入到配置中 currentCache = cache;//本类中保留一份 return cache; }

CacheBuilder

public Cache build() { setDefaultImplementations(); Cache cache = newBaseCacheInstance(implementation, id);//创建基本的缓存 setCacheProperties(cache);//填充cache属性 if (PerpetualCache.class.equals(cache.getClass())) { for (Class<? extends Cache> decorator : decorators) { cache = newCacheDecoratorInstance(decorator, cache);//添加装饰器,默认只有LruCache setCacheProperties(cache); } cache = setStandardDecorators(cache);//根据条件添加其他Cache和默认Cache } else if (!LoggingCache.class.isAssignableFrom(cache.getClass())) { cache = new LoggingCache(cache);//如果不是LoggingCache的子类添加日志装饰器 } return cache; } private void setDefaultImplementations() {//如果implementation为空且decorators为空用默认的 if (implementation == null) { implementation = PerpetualCache.class; if (decorators.isEmpty()) { decorators.add(LruCache.class); } } } private Cache setStandardDecorators(Cache cache) { try { MetaObject metaCache = SystemMetaObject.forObject(cache); if (size != null && metaCache.hasSetter("size")) { metaCache.setValue("size", size); } if (clearInterval != null) {//如果设置了清理时间则添加定时策略 cache = new ScheduledCache(cache); ((ScheduledCache) cache).setClearInterval(clearInterval); } if (readWrite) {//读写策略 cache = new SerializedCache(cache); } cache = new LoggingCache(cache);//默认增加日志 cache = new SynchronizedCache(cache);//默认添加同步 if (blocking) { cache = new BlockingCache(cache);//设置了阻塞,创建阻塞策略 } return cache; } catch (Exception e) { throw new CacheException("Error building standard cache decorators. Cause: " + e, e); } }

在默认情况下添加的Cache类型有:PerpetualCache、LruCache、LoggingCache、SynchronizedCache。

2)<cache-ref> 引用已有缓存

<cache-ref namespace="org.apache.ibatis.builder.CachedAuthorMapper"/> private void cacheRefElement(XNode context) { if (context != null) { //将本mapper中的namespace作为key,指向的namespace作为value configuration.addCacheRef(builderAssistant.getCurrentNamespace(), context.getStringAttribute("namespace")); CacheRefResolver cacheRefResolver = new CacheRefResolver(builderAssistant, context.getStringAttribute("namespace")); try { cacheRefResolver.resolveCacheRef(); } catch (IncompleteElementException e) { configuration.addIncompleteCacheRef(cacheRefResolver); } } }

类CacheRefResolver的resolveCacheRef()方法还是委托MapperBuilderAssistant类中的useCacheRef方法

public Cache resolveCacheRef() {//cacheRefNamespace是cache-ref指向缓存的namespace return assistant.useCacheRef(cacheRefNamespace); } //类MapperBuilderAssistant public Cache useCacheRef(String namespace) { //namespace为null,则抛出异常 try { unresolvedCacheRef = true;//标记当前未解析完成 Cache cache = configuration.getCache(namespace);//获取已有缓存 //cache==null,则抛出异常 currentCache = cache;//MapperBuilderAssistant中保留一份当前缓存 unresolvedCacheRef = false;//标记已解析完成 return cache; } catch (IllegalArgumentException e) { throw new IncompleteElementException.... } }

3)<resultMap> 结果集映射

<resultMap id="selectAuthor" type="org.apache.ibatis.domain.blog.Author"> <id column="id" property="id" /> <result property="username" column="username" /> <result property="password" column="password" /> <result property="email" column="email" /> <result property="bio" column="bio" /> <result property="favouriteSection" column="favourite_section" /> </resultMap> <resultMap id="selectImmutableAuthor" type="org.apache.ibatis.domain.blog.ImmutableAuthor"> <constructor> <idArg column="id" javaType="_int" /> <arg column="username" javaType="string" /> <arg column="password" javaType="string" /> <arg column="email" javaType="string" /> <arg column="bio" javaType="string" /> <arg column="favourite_section" javaType="org.apache.ibatis.domain.blog.Section" /> </constructor> </resultMap> private ResultMap resultMapElement(XNode resultMapNode) throws Exception { return resultMapElement(resultMapNode, Collections.<ResultMapping> emptyList()); } private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings) throws Exception { String id = resultMapNode.getStringAttribute("id");//获取id属性 String type = resultMapNode.getStringAttribute("type"); String extend = resultMapNode.getStringAttribute("extends"); Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping"); Class<?> typeClass = resolveClass(type); Discriminator discriminator = null; List<ResultMapping> resultMappings = new ArrayList<ResultMapping>(); resultMappings.addAll(additionalResultMappings); List<XNode> resultChildren = resultMapNode.getChildren(); for (XNode resultChild : resultChildren) { if ("constructor".equals(resultChild.getName())) {//构造方式处理 processConstructorElement(resultChild, typeClass, resultMappings); } else if ("discriminator".equals(resultChild.getName())) {//鉴别器处理 discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings); } else {//处理<id>、<result>、<association>、<collection>等 List<ResultFlag> flags = new ArrayList<ResultFlag>(); if ("id".equals(resultChild.getName())) { flags.add(ResultFlag.ID); } resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags)); } } ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping); return resultMapResolver.resolve();//将结果稍作处理放入configuration中的resultMaps中 } private ResultMapping buildResultMappingFromContext(XNode context, Class<?> resultType, List<ResultFlag> flags) throws Exception { String property; if (flags.contains(ResultFlag.CONSTRUCTOR)) { property = context.getStringAttribute("name"); } else { property = context.getStringAttribute("property"); } //...省略获取column、javaType、jdbcType、javaTypeClass、typeHandlerClass 等 JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType); //调用建造者辅助类中的buildResultMapping方法创建ResultMapping对象 return builderAssistant.buildResultMapping(resultType, property, column, javaTypeClass, jdbcTypeEnum, nestedSelect, nestedResultMap, notNullColumn, columnPrefix, typeHandlerClass, flags, resultSet, foreignColumn, lazy); } //处理<constructor>标签 private void processConstructorElement(XNode resultChild, Class<?> resultType, List<ResultMapping> resultMappings) throws Exception { List<XNode> argChildren = resultChild.getChildren(); for (XNode argChild : argChildren) { List<ResultFlag> flags = new ArrayList<ResultFlag>(); flags.add(ResultFlag.CONSTRUCTOR);//标记为constructor类型 if ("idArg".equals(argChild.getName())) {//有idArg说明有id属性 flags.add(ResultFlag.ID); } //后面解析和上面一样了 resultMappings.add(buildResultMappingFromContext(argChild, resultType, flags)); } }

4)<sql>  复用的sql片段

<sql id="all_column"> id, username, password, email, bio, favourite_section </sql> <select id="selectAuthor" parameterMap="selectAuthor" resultMap="selectAuthor"> select <include refid="all_column" /> from author where id = ? </select> private void sqlElement(List<XNode> list, String requiredDatabaseId) throws Exception { for (XNode context : list) { String databaseId = context.getStringAttribute("databaseId"); String id = context.getStringAttribute("id"); id = builderAssistant.applyCurrentNamespace(id, false); //校验databaseId与configuration中的一致,本例中为null,返回true if (databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId)) { sqlFragments.put(id, context);//片段缓存到XMLMapperBuilder中的Map<String, XNode> sqlFragments中 } } }

 

最新回复(0)