交流
商城
MCN
登入
注册
首页
提问
分享
讨论
建议
公告
动态
发表新帖
发表新帖
mybatis第2-1 章:XMLMapperBuilder
分享
未结
0
987
李延
LV6
2021-06-06
悬赏:20积分
# 作用 解析mapper的xml数据,并进行注册 # 主要方法 ```java public void parse() { //判断文件是否已经加载过 if (!configuration.isResourceLoaded(resource)) { //解析xml configurationElement(parser.evalNode("/mapper")); //标记已经加载的文件 configuration.addLoadedResource(resource); bindMapperForNamespace(); } parsePendingResultMaps(); parsePendingCacheRefs(); parsePendingStatements(); } ``` 我们依次看一下没个步骤 ## configurationElement ```java private void configurationElement(XNode context) { try { //获取namespace。也就是我们平常写的mapper接口 String namespace = context.getStringAttribute("namespace"); //判断null情况 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")); //读取parameterMap parameterMapElement(context.evalNodes("/mapper/parameterMap")); //读取resultMap resultMapElements(context.evalNodes("/mapper/resultMap")); //读取sql sqlElement(context.evalNodes("/mapper/sql")); //读取各种语句 buildStatementFromContext(context.evalNodes("select|insert|update|delete")); } catch (Exception e) { throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e); } } ``` 对于不同步骤的解析过程,我们逐一分析 ## cacheRefElement 引用其他mapper的二级缓存配置 ```java private void cacheRefElement(XNode context) { if (context != null) { configuration.addCacheRef(builderAssistant.getCurrentNamespace(), context.getStringAttribute("namespace")); CacheRefResolver cacheRefResolver = new CacheRefResolver(builderAssistant, context.getStringAttribute("namespace")); try { cacheRefResolver.resolveCacheRef(); } catch (IncompleteElementException e) { configuration.addIncompleteCacheRef(cacheRefResolver); } } } ``` 我们看到通过读取cache-ref的namespace属性,读取到被引用的对象。并将结果保存在configuration的cacheRefMap中 ## cacheElement 设置二级缓存配置 这里我们加载的是cache标签。 ```java private void cacheElement(XNode context) throws Exception { if (context != null) { String type = context.getStringAttribute("type", "PERPETUAL"); Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type); 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(); builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props); } } // builderAssistant.useNewCache方法 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; } ``` 这里我们看到,最后将所有相关配置封装为Cache对象,并保存在configuration的caches中。 ## parameterMap 参数map的处理 这里代码就不详细说明了。最后是被封装为ParameterMapping对象,并保存在configuration的parameterMaps中。 ## resultMapElements 返回值map的处理 这里代码就不详细说明了。最后是被封装为ResultMapResolver对象,并保存在configuration的incompleteResultMaps中。 ## sqlElement 处理sql语句 这个没有被保存在configuration中,而是临时保存在当前类的sqlFragments中,value就是未解析的xml文本。 ## buildStatementFromContext 所有需要执行的sql方法 ```java private void buildStatementFromContext(List<XNode> list) { if (configuration.getDatabaseId() != null) { buildStatementFromContext(list, configuration.getDatabaseId()); } buildStatementFromContext(list, null); } private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) { for (XNode context : list) { final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId); try { statementParser.parseStatementNode(); } catch (IncompleteElementException e) { configuration.addIncompleteStatement(statementParser); } } } ``` 这里我们看到交给了XMLStatementBuilder对象处理。我们看一下这个方法 ```java public void parseStatementNode() { //获取id属性,也就是我们的方法名称 String id = context.getStringAttribute("id"); String databaseId = context.getStringAttribute("databaseId"); //判断是否有重名的方法 if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) { return; } //读取不同的属性 Integer fetchSize = context.getIntAttribute("fetchSize"); Integer timeout = context.getIntAttribute("timeout"); String parameterMap = context.getStringAttribute("parameterMap"); String parameterType = context.getStringAttribute("parameterType"); Class<?> parameterTypeClass = resolveClass(parameterType); String resultMap = context.getStringAttribute("resultMap"); String resultType = context.getStringAttribute("resultType"); String lang = context.getStringAttribute("lang"); LanguageDriver langDriver = getLanguageDriver(lang); Class<?> resultTypeClass = resolveClass(resultType); String resultSetType = context.getStringAttribute("resultSetType"); StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString())); ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType); //获取sql类型 String nodeName = context.getNode().getNodeName(); SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH)); boolean isSelect = sqlCommandType == SqlCommandType.SELECT; boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect); boolean useCache = context.getBooleanAttribute("useCache", isSelect); boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false); // Include Fragments before parsing XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant); includeParser.applyIncludes(context.getNode()); // Parse selectKey after includes and remove them. processSelectKeyNodes(id, parameterTypeClass, langDriver); // Parse the SQL (pre: <selectKey> and <include> were parsed and removed) SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass); String resultSets = context.getStringAttribute("resultSets"); String keyProperty = context.getStringAttribute("keyProperty"); String keyColumn = context.getStringAttribute("keyColumn"); KeyGenerator keyGenerator; String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX; keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true); if (configuration.hasKeyGenerator(keyStatementId)) { keyGenerator = configuration.getKeyGenerator(keyStatementId); } else { keyGenerator = context.getBooleanAttribute("useGeneratedKeys", configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType)) ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE; } builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered, keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets); } ``` 上面代码中我们看到sql语句被封装为SqlSource对象,并再次封装为MappedStatement类型。保存在configuration的mappedStatements中。 其中key为接口全路径+ 方法名称。 在SqlSource中,sql如果是动态sql。会一list的形式保存 如果是下面的sql ```java select * from user where id = #{id} <if test="id != null and id != ''"> id=#{id} </if> <if test="username != null and username != ''"> and username like concat('%',#{username},'%') </if> <if test="password != null and password != ''"> and password=#{password} </if> ``` 则保存的sql为:  # 总结 我们看到在xml的数据中,不同的xml片段都会被保存在configuration中。其中大部分为map形式保存,key为接口全路径+方法名称
回帖
消灭零回复
提交回复
热议榜
java 相关知识分享
8
好的程序员与不好的程序员
6
写给工程师的十条精进原则
5
spring boot以jar包运行配置的logback日志文件没生成
5
一步一步分析SpringBoot启动源码(一)
5
MockMvc测试
5
【吐槽向】是不是有个吐槽的板块比较好玩
4
logstash jdbc同步mysql多表数据到elasticsearch
3
IntelliJ IDEA 优质License Server
3
.gitignore忽略规则
3
SpringBoot启动源码分析
3
一步一步分析SpringBoot启动源码(三)
3
2
一步一步分析SpringBoot启动源码(二)
2
积分不够将无法发表新帖
2
官方产品
Meta-Boot - 基于MCN
MCN - 快速构建SpringBoot应用
微信扫码关注公众号