山东省建设工会网站,有没有帮人做简历的网站,免备案域名有哪些,建设网站重庆文章目录实例一、SqlSource处理入口二、SqlSource处理逻辑1、XMLScriptBuilder 构造方法2、解析动态sql3、DynamicSqlSource4、RawSqlSource解析sql#xff08;1#xff09;parse方法解析sql写在后面实例
此处我们分析的sql#xff1a;
select idselectBlog1parse方法解析sql写在后面实例
此处我们分析的sql
select idselectBlog resultTypecom.demo.Blog useCachetrueselect * from blog where id #{id}
/select一、SqlSource处理入口
在处理配置文件时会处理Mapper.xml文件
// org.apache.ibatis.builder.xml.XMLConfigBuilder#parseConfiguration
private void parseConfiguration(XNode root) {try {// issue #117 read properties firstpropertiesElement(root.evalNode(properties));Properties settings settingsAsProperties(root.evalNode(settings));loadCustomVfs(settings);loadCustomLogImpl(settings);typeAliasesElement(root.evalNode(typeAliases));pluginElement(root.evalNode(plugins));objectFactoryElement(root.evalNode(objectFactory));objectWrapperFactoryElement(root.evalNode(objectWrapperFactory));reflectorFactoryElement(root.evalNode(reflectorFactory));settingsElement(settings);// read it after objectFactory and objectWrapperFactory issue #631environmentsElement(root.evalNode(environments));databaseIdProviderElement(root.evalNode(databaseIdProvider));typeHandlerElement(root.evalNode(typeHandlers));mapperElement(root.evalNode(mappers)); // 处理mapper标签} catch (Exception e) {throw new BuilderException(Error parsing SQL Mapper Configuration. Cause: e, e);}
}// org.apache.ibatis.builder.xml.XMLConfigBuilder#mapperElement
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);try(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);try(InputStream inputStream Resources.getUrlAsStream(url)){XMLMapperBuilder mapperParser new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());mapperParser.parse(); // 解析Mapper.xml}} 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.);}}}}
}// org.apache.ibatis.builder.xml.XMLMapperBuilder#parse
public void parse() {if (!configuration.isResourceLoaded(resource)) {configurationElement(parser.evalNode(/mapper));configuration.addLoadedResource(resource);bindMapperForNamespace();}parsePendingResultMaps();parsePendingCacheRefs();// 预处理StatementparsePendingStatements();
}// org.apache.ibatis.builder.xml.XMLMapperBuilder#parsePendingStatements
private void parsePendingStatements() {CollectionXMLStatementBuilder incompleteStatements configuration.getIncompleteStatements();synchronized (incompleteStatements) {IteratorXMLStatementBuilder iter incompleteStatements.iterator();while (iter.hasNext()) {try {iter.next().parseStatementNode(); // 处理nodeiter.remove();} catch (IncompleteElementException e) {// Statement is still missing a resource...}}}
}// org.apache.ibatis.builder.xml.XMLStatementBuilder#parseStatementNode
public void parseStatementNode() {String id context.getStringAttribute(id);String databaseId context.getStringAttribute(databaseId);if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {return;}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 parsingXMLIncludeTransformer includeParser new XMLIncludeTransformer(configuration, builderAssistant);includeParser.applyIncludes(context.getNode());String parameterType context.getStringAttribute(parameterType);Class? parameterTypeClass resolveClass(parameterType);String lang context.getStringAttribute(lang);LanguageDriver langDriver getLanguageDriver(lang);// Parse selectKey after includes and remove them.processSelectKeyNodes(id, parameterTypeClass, langDriver);// Parse the SQL (pre: selectKey and include were parsed and removed)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;}// 创建SqlSource核心逻辑SqlSource sqlSource langDriver.createSqlSource(configuration, context, parameterTypeClass);StatementType statementType StatementType.valueOf(context.getStringAttribute(statementType, StatementType.PREPARED.toString()));Integer fetchSize context.getIntAttribute(fetchSize);Integer timeout context.getIntAttribute(timeout);String parameterMap context.getStringAttribute(parameterMap);String resultType context.getStringAttribute(resultType);Class? resultTypeClass resolveClass(resultType);String resultMap context.getStringAttribute(resultMap);String resultSetType context.getStringAttribute(resultSetType);ResultSetType resultSetTypeEnum resolveResultSetType(resultSetType);if (resultSetTypeEnum null) {resultSetTypeEnum configuration.getDefaultResultSetType();}String keyProperty context.getStringAttribute(keyProperty);String keyColumn context.getStringAttribute(keyColumn);String resultSets context.getStringAttribute(resultSets);builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,resultSetTypeEnum, flushCache, useCache, resultOrdered,keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}我们找到了处理SqlSource的核心入口。
二、SqlSource处理逻辑
创建SqlSource解析SQL封装SQL语句未参数绑定和入参信息。
// org.apache.ibatis.scripting.xmltags.XMLLanguageDriver#createSqlSource(org.apache.ibatis.session.Configuration, org.apache.ibatis.parsing.XNode, java.lang.Class?)
Override
public SqlSource createSqlSource(Configuration configuration, XNode script, Class? parameterType) {// 初始化了动态SQL标签处理器XMLScriptBuilder builder new XMLScriptBuilder(configuration, script, parameterType);// 解析动态SQLreturn builder.parseScriptNode();
}1、XMLScriptBuilder 构造方法
public XMLScriptBuilder(Configuration configuration, XNode context, Class? parameterType) {super(configuration);this.context context;this.parameterType parameterType;// 初始化动态SQL中的节点处理器集合initNodeHandlerMap();
}// 动态sql所有的节点
private void initNodeHandlerMap() {nodeHandlerMap.put(trim, new TrimHandler());nodeHandlerMap.put(where, new WhereHandler());nodeHandlerMap.put(set, new SetHandler());nodeHandlerMap.put(foreach, new ForEachHandler());nodeHandlerMap.put(if, new IfHandler());nodeHandlerMap.put(choose, new ChooseHandler());nodeHandlerMap.put(when, new IfHandler());nodeHandlerMap.put(otherwise, new OtherwiseHandler());nodeHandlerMap.put(bind, new BindHandler());
}2、解析动态sql
XMLScriptBuilder#parseScriptNode用于解析动态sql
public SqlSource parseScriptNode() {// 解析select\insert\ update\delete标签中的SQL语句最终将解析到的SqlNode封装到MixedSqlNode中的List集合中// ****将带有${}号的SQL信息封装到TextSqlNode// ****将带有#{}号的SQL信息封装到StaticTextSqlNode// ****将动态SQL标签中的SQL信息分别封装到不同的SqlNode中MixedSqlNode rootSqlNode parseDynamicTags(context);SqlSource sqlSource;if (isDynamic) {// 如果SQL中包含${}和动态SQL语句则将SqlNode封装到DynamicSqlSource// 最终结果是select id from blog where id ${id}sqlSource new DynamicSqlSource(configuration, rootSqlNode);} else {// 如果SQL中包含#{}则将SqlNode封装到RawSqlSource中并指定parameterType// 最终的结果是select id from blog where id ?sqlSource new RawSqlSource(configuration, rootSqlNode, parameterType);}return sqlSource;
}parseDynamicTags解析sql语句
解析select\insert\ update\delete标签中的SQL语句最终将解析到的SqlNode封装到MixedSqlNode中的List集合中。
// org.apache.ibatis.scripting.xmltags.XMLScriptBuilder#parseDynamicTags
protected MixedSqlNode parseDynamicTags(XNode node) {ListSqlNode contents new ArrayList();//获取select\insert\update\delete4个标签的子节点子节点包括元素节点和文本节点NodeList children node.getNode().getChildNodes();for (int i 0; i children.getLength(); i) {// 获取标签内的原始自定义sqlselect * from blog where id #{id}XNode child node.newXNode(children.item(i));// 处理文本节点if (child.getNode().getNodeType() Node.CDATA_SECTION_NODE || child.getNode().getNodeType() Node.TEXT_NODE) {String data child.getStringBody();// 将文本内容封装到SqlNode中还是原始sqlTextSqlNode textSqlNode new TextSqlNode(data);// SQL语句中带有${}的话就表示是dynamic的if (textSqlNode.isDynamic()) {contents.add(textSqlNode);isDynamic true;} else {// SQL语句中除了${}和下面的动态SQL标签就表示是static的// StaticTextSqlNode的apply只是进行字符串的追加操作contents.add(new StaticTextSqlNode(data));}//处理元素节点} else if (child.getNode().getNodeType() Node.ELEMENT_NODE) { // issue #628String nodeName child.getNode().getNodeName();// 动态SQL标签处理器// 策略模式NodeHandler handler nodeHandlerMap.get(nodeName);if (handler null) {throw new BuilderException(Unknown element nodeName in SQL statement.);}handler.handleNode(child, contents);// 动态SQL标签是dynamic的isDynamic true;}}return new MixedSqlNode(contents);
}最终返回了携带了原始sql的对象。
3、DynamicSqlSource
如果SQL中包含${}和动态SQL语句则将SqlNode封装到DynamicSqlSource。
咱们此处研究的是简单的、包含#{id}的sql暂不研究动态SQL。
select id from blog where id ${id}比如说以上sql会原封不动的生成SqlSource并不会进行解析。
4、RawSqlSource解析sql
如果SQL中包含#{}则将SqlNode封装到RawSqlSource中并指定parameterType
我们看一下RawSqlSource的构造方法
public RawSqlSource(Configuration configuration, SqlNode rootSqlNode, Class? parameterType) {this(configuration, getSql(configuration, rootSqlNode), parameterType);
}public RawSqlSource(Configuration configuration, String sql, Class? parameterType) {// 解析SQL语句SqlSourceBuilder sqlSourceParser new SqlSourceBuilder(configuration);// 获取入参类型Class? clazz parameterType null ? Object.class : parameterType;// 开始解析sqlSource sqlSourceParser.parse(sql, clazz, new HashMap());
}1parse方法解析sql
// org.apache.ibatis.builder.SqlSourceBuilder#parse
public SqlSource parse(String originalSql, Class? parameterType, MapString, Object additionalParameters) {ParameterMappingTokenHandler handler new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);// 创建分词解析器GenericTokenParser parser new GenericTokenParser(#{, }, handler);String sql;// 解析#{}if (configuration.isShrinkWhitespacesInSql()) {sql parser.parse(removeExtraWhitespaces(originalSql)); // 处理额外的空格} else {sql parser.parse(originalSql); // 解析最终sql为select * from blog where id ?}// 将解析之后的SQL信息封装到StaticSqlSource对象中// SQL字符串是带有?号的字符串?相关的参数信息封装到ParameterMapping集合中return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
}调用GenericTokenParser的parse进行解析
// org.apache.ibatis.parsing.GenericTokenParser#parse
public String parse(String text) {if (text null || text.isEmpty()) {return ;}// search open tokenint start text.indexOf(openToken);if (start -1) {return text;}char[] src text.toCharArray();int offset 0;final StringBuilder builder new StringBuilder();StringBuilder expression null;do {if (start 0 src[start - 1] \\) {// this open token is escaped. remove the backslash and continue.builder.append(src, offset, start - offset - 1).append(openToken);offset start openToken.length();} else {// found open token. lets search close token.if (expression null) {expression new StringBuilder();} else {expression.setLength(0);}builder.append(src, offset, start - offset);offset start openToken.length();int end text.indexOf(closeToken, offset);while (end -1) {if (end offset src[end - 1] \\) {// this close token is escaped. remove the backslash and continue.expression.append(src, offset, end - offset - 1).append(closeToken);offset end closeToken.length();end text.indexOf(closeToken, offset);} else {expression.append(src, offset, end - offset);break;}}if (end -1) {// close token was not found.builder.append(src, start, src.length - start);offset src.length;} else {builder.append(handler.handleToken(expression.toString())); // 该方法会返回一个 ? offset end closeToken.length();}}start text.indexOf(openToken, offset);} while (start -1);if (offset src.length) {builder.append(src, offset, src.length - offset);}return builder.toString();
}最终相当于是逐个字符进行解析然后将#{id}替换成了 ?
写在后面
如果本文对你有帮助请点赞收藏关注一下吧 ~