2019獨(dú)角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>
Mybatis3源碼分析(05)-加載Configuration-加載MappedStatement 博客分類: java mybatis
MappedStatement說明
一個(gè)MappedStatement對象對應(yīng)Mapper配置文件中的一個(gè)select/update/insert/delete節(jié)點(diǎn),主要描述的是一條SQL語句。其屬性有
[java] view plain copy
//節(jié)點(diǎn)中的id屬性加要命名空間??private?String?id;??//直接從節(jié)點(diǎn)屬性中取??private?Integer?fetchSize;??//直接從節(jié)點(diǎn)屬性中取??private?Integer?timeout;??private?StatementType?statementType;??private?ResultSetType?resultSetType;??//對應(yīng)一條SQL語句??private?SqlSource?sqlSource;????//每條語句都對就一個(gè)緩存,如果有的話。??private?Cache?cache;??//這個(gè)已經(jīng)過時(shí)了??private?ParameterMap?parameterMap;??private?List<ResultMap>?resultMaps;??private?boolean?flushCacheRequired;??private?boolean?useCache;??private?boolean?resultOrdered;??//SQL的類型,select/update/insert/detete??private?SqlCommandType?sqlCommandType;??private?KeyGenerator?keyGenerator;??private?String[]?keyProperties;??private?String[]?keyColumns;????//是否有內(nèi)映射??private?boolean?hasNestedResultMaps;??private?String?databaseId;??private?Log?statementLog;??private?LanguageDriver?lang;??private?String[]?resultSets;?? 上面屬性都比較簡單,復(fù)雜的是SqlSource,下面有詳細(xì)的描述!
XMLStatementBuilder.parseStatementNode()方法
resultMap元素的解析已經(jīng)分析完畢。與resultMap不一樣,XmlMapperBuilder在解析select/update /insert/delete的元素時(shí)會創(chuàng)建一個(gè)XMLStatementBuilder對象,解析的工作交由其方法 parseStatementNode()方法完成。
[java] view plain copy
private?void?buildStatementFromContext(List<XNode>?list,?String?requiredDatabaseId)?{??????for?(XNode?context?:?list)?{????????//一個(gè)select/update/insert/delete元素創(chuàng)建一個(gè)XMLStatementBuilder對象????????final?XMLStatementBuilder?statementParser?=?new?XMLStatementBuilder(configuration,?builderAssistant,?context,?requiredDatabaseId);????????try?{??????????//將元素解析成MappedStatemenet對象,并加入到Configuration中去??????????statementParser.parseStatementNode();????????}?catch?(IncompleteElementException?e)?{??????????configuration.addIncompleteStatement(statementParser);????????}??????}?? 如下是parseStatementNode()方法的代碼
[java] view plain copy
public?void?parseStatementNode()?{??????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");??????//Statement的類型,對應(yīng)jdbc里的三個(gè)類型:Statement、PreparedStatement、CallableStatement,默認(rèn)使用PreparedStatement??????StatementType?statementType?=?StatementType.valueOf(context.getStringAttribute("statementType",?StatementType.PREPARED.toString()));??????//這個(gè)也是跟jdbc里相對應(yīng)的,一般采用默認(rèn)即可??????ResultSetType?resultSetTypeEnum?=?resolveResultSetType(resultSetType);???????????//Sql的類型,select/update/insert/delete??????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);????????????//生成SqlSource對象,這個(gè)對象非常重要,接下來詳細(xì)分析??????//?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");????????????//自動生成key,這里也不做討論??????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))??????????????new?Jdbc3KeyGenerator()?:?new?NoKeyGenerator();??????}??????//生成MappedStatement對象,并加到Configuration中??????builderAssistant.addMappedStatement(id,?sqlSource,?statementType,?sqlCommandType,??????????fetchSize,?timeout,?parameterMap,?parameterTypeClass,?resultMap,?resultTypeClass,??????????resultSetTypeEnum,?flushCache,?useCache,?resultOrdered,???????????keyGenerator,?keyProperty,?keyColumn,?databaseId,?langDriver,?resultSets);????}?? 上在方法里附件解析一些基本的屬性外還有兩個(gè)主要的部分
SqlSource的構(gòu)建過程
[java] view plain copy SqlSource?sqlSource?=?langDriver.createSqlSource(configuration,?context,?parameterTypeClass);?? MappedStatement的構(gòu)建過程 [java] view plain copy builderAssistant.addMappedStatement(id,?sqlSource,?statementType,?sqlCommandType,?????????fetchSize,?timeout,?parameterMap,?parameterTypeClass,?resultMap,?resultTypeClass,?????????resultSetTypeEnum,?flushCache,?useCache,?resultOrdered,??????????keyGenerator,?keyProperty,?keyColumn,?databaseId,?langDriver,?resultSets);?? SqlSource構(gòu)建過程
SqlSource接口
[java] view plain copy
/**??*?Represents?the?content?of?a?mapped?statement?read?from?an?XML?file?or?an?annotation.???*?It?creates?the?SQL?that?will?be?passed?to?the?database?out?of?the?input?parameter?received?from?the?user.??*??*?@author?Clinton?Begin??*/??public?interface?SqlSource?{??????BoundSql?getBoundSql(Object?parameterObject);????}?? SqlSource表示從mapper.xml或注解中讀取的sql內(nèi)容,該sql一般還不能都被直接執(zhí)行,例如
[html] view plain copy
<select?id="selectUserDetail"?resultMap="detailUserResultMap">??????<!--CDATA里內(nèi)容會都解析成一個(gè)SqlSource對象-->??????????????????<![CDATA[?????????????select?user_id,user_name,user_type,cust_id?from?tf_f_user?a?where?a.user_id=#${userId}?????????]]>/select>?? SqlSource只有一個(gè)方法:getBoundSql(paramenterObject),其中paramenterObject為運(yùn)行sql里的實(shí)際參數(shù)
BoundSql
[java] view plain copy
/**??*?An?actual?SQL?String?got?form?an?{@link?SqlSource}?after?having?processed?any?dynamic?content.??*?The?SQL?may?have?SQL?placeholders?"?"?and?an?list?(ordered)?of?an?parameter?mappings???*?with?the?additional?information?for?each?parameter?(at?least?the?property?name?of?the?input?object?to?read???*?the?value?from).???*?</br>??*?Can?also?have?additional?parameters?that?are?created?by?the?dynamic?language?(for?loops,?bind...).??*/??/**??*?@author?Clinton?Begin??*/??public?class?BoundSql?{??????private?String?sql;????private?List<ParameterMapping>?parameterMappings;????private?Object?parameterObject;????private?Map<String,?Object>?additionalParameters;????private?MetaObject?metaParameters;??????public?BoundSql(Configuration?configuration,?String?sql,?List<ParameterMapping>?parameterMappings,?Object?parameterObject)?{??????this.sql?=?sql;??????this.parameterMappings?=?parameterMappings;??????this.parameterObject?=?parameterObject;??????this.additionalParameters?=?new?HashMap<String,?Object>();??????this.metaParameters?=?configuration.newMetaObject(additionalParameters);????}??????public?String?getSql()?{??????return?sql;????}??????public?List<ParameterMapping>?getParameterMappings()?{??????return?parameterMappings;????}??????public?Object?getParameterObject()?{??????return?parameterObject;????}??????public?boolean?hasAdditionalParameter(String?name)?{??????return?metaParameters.hasGetter(name);????}??????public?void?setAdditionalParameter(String?name,?Object?value)?{??????metaParameters.setValue(name,?value);????}???????public?Object?getAdditionalParameter(String?name)?{??????return?metaParameters.getValue(name);?????}??}???????? SqlBound代碼并不多,就是一個(gè)普通的java對象,有兩個(gè)屬性非常重要
sql:看代碼里的注解,這個(gè)sql已經(jīng)是經(jīng)過了一些處理,可以被jdbc執(zhí)行的了。xml里配置的sql可能有占位符#{username},這里的sql占位符已經(jīng)被替換成"?"號了。parameterMappings:執(zhí)行sql對象的實(shí)際的參數(shù)。由此可以判斷,每執(zhí)行一條sql都會創(chuàng)建一個(gè)BoundSql對象。 SqlSource和BoundSql本身并不復(fù)雜,復(fù)雜的是這兩個(gè)對象被創(chuàng)建的過程。
LanguageDriver
SqlSource對象是通過LanguageDriver對象構(gòu)建的,在mapper.xml配置sql里可以通過lang屬性指定一個(gè) LanguageDriver,但我們通常不會這樣子做。當(dāng)lang屬性沒有配置時(shí),Mybatis會屬性默認(rèn)給一個(gè)。這個(gè)默認(rèn)的 LanguageDriver在Configuration的構(gòu)造方法中定義的:
[java] view plain copy
public?Configuration()?{??????...??????languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);??????languageRegistry.register(RawLanguageDriver.class);????}?? 馬上來看XMLLanguageDriver.createSqlSource()方法
[java] view plain copy
public?SqlSource?createSqlSource(Configuration?configuration,?XNode?script,?Class<?>?parameterType)?{??????XMLScriptBuilder?builder?=?new?XMLScriptBuilder(configuration,?script,?parameterType);??????return?builder.parseScriptNode();????}?? XMLScriptBuilder
XMLScriptBuilder.parseScriptNode()方法
[java] view plain copy
public?SqlSource?parseScriptNode()?{??????//將一個(gè)sql內(nèi)容解析成多個(gè)SqlNode??????List<SqlNode>?contents?=?parseDynamicTags(context);??????//將多個(gè)SqlNode組合一個(gè)SqlNode??????MixedSqlNode?rootSqlNode?=?new?MixedSqlNode(contents);??????SqlSource?sqlSource?=?null;??????//判斷sql是否是動態(tài)的??????if?(isDynamic)?{????????//生成動態(tài)的SqlSource????????sqlSource?=?new?DynamicSqlSource(configuration,?rootSqlNode);??????}?else?{????????//生成靜態(tài)的SqlSource????????sqlSource?=?new?RawSqlSource(configuration,?rootSqlNode,?parameterType);??????}??????return?sqlSource;????}?? 再看parseDynamicTagS(context)方法
[java] view plain copy
private?List<SqlNode>?parseDynamicTags(XNode?node)?{??????//一個(gè)sql會被解析成多個(gè)SqlNode,稍后會有示例詳細(xì)說明??????List<SqlNode>?contents?=?new?ArrayList<SqlNode>();??????NodeList?children?=?node.getNode().getChildNodes();??????for?(int?i?=?0;?i?<?children.getLength();?i++)?{????????XNode?child?=?node.newXNode(children.item(i));????????if?(child.getNode().getNodeType()?==?Node.CDATA_SECTION_NODE?||?child.getNode().getNodeType()?==?Node.TEXT_NODE)?{??????????//如果這個(gè)Node只包含文本??????????String?data?=?child.getStringBody("");??????????//生成一個(gè)TextSqlNode??????????TextSqlNode?textSqlNode?=?new?TextSqlNode(data);??????????//判斷是否是動態(tài)的,如果文本里包含占位符,如#{username}或{table_name},isDynamic()方法就會返回true??????????if?(textSqlNode.isDynamic())?{????????????contents.add(textSqlNode);????????????isDynamic?=?true;??????????}?else?{????????????contents.add(new?StaticTextSqlNode(data));??????????}????????}?else?if?(child.getNode().getNodeType()?==?Node.ELEMENT_NODE)?{?//?issue?#628??????????//如果是有xml標(biāo)簽的Node,交由Handler處理,同時(shí)被認(rèn)為是動態(tài)的??????????String?nodeName?=?child.getNode().getNodeName();??????????NodeHandler?handler?=?nodeHandlers.get(nodeName);??????????if?(handler?==?null)?{????????????throw?new?BuilderException("Unknown?element?<"?+?nodeName?+?">?in?SQL?statement.");??????????}??????????handler.handleNode(child,?contents);????????????????isDynamic?=?true;????????}??????}??????return?contents;????}?? 再看看nodeHandlers都有那些
[java] view plain copy
private?Map<String,?NodeHandler>?nodeHandlers?=?new?HashMap<String,?NodeHandler>()?{??????private?static?final?long?serialVersionUID?=?7123056019193266281L;????????{????????//Mybatis3動態(tài)sql都支持那些配置,這里就很清楚啦????????put("trim",?new?TrimHandler());????????put("where",?new?WhereHandler());????????put("set",?new?SetHandler());????????put("foreach",?new?ForEachHandler());????????put("if",?new?IfHandler());????????put("choose",?new?ChooseHandler());????????put("when",?new?IfHandler());????????put("otherwise",?new?OtherwiseHandler());????????put("bind",?new?BindHandler());??????}????};?? 看到這里基本上能了解sql是怎么被解析的啦!舉例說明:
[html] view plain copy
<select?id="selectUserDetail"?resultMap="detailUserResultMap">??????????<![CDATA[?????????????select?user_id,user_name,user_type,cust_id?--這里一行會解析成一個(gè)StaticTextSqlNode?????????????????from?tf_f_user?a?--這里一行也會解析成一個(gè)StaticTextSqlNode?????????????????where?a.user_id=#{userId}?--這行會被解析成TextSqlNode,并且isDynamic被設(shè)置成true,因?yàn)橛姓嘉环???????????????????????????????????????????--這個(gè)空行也解析成一個(gè)StaticTextSqlNode?????????]]><!--?這四個(gè)SqlNode會被組合成一個(gè)MixedSqlNode?-->??????</select>?? 再來個(gè)動態(tài)sql的:
[html] view plain copy
<select?id="selectUserDetail"?resultMap="detailUserResultMap">??????????<![CDATA[?????????????select?user_id,user_name,user_type,cust_id?--這里一行會解析成一個(gè)StaticTextSqlNode?????????????????from?tf_f_user?a?--這里一行也會解析成一個(gè)StaticTextSqlNode?????????????????where?a.user_id=#{userId}?--這行會被解析成TextSqlNode,并且isDynamic被設(shè)置成true,因?yàn)橛姓嘉环???????????????????????????????????????????--這個(gè)空行也解析成一個(gè)StaticTextSqlNode?????????]]>??????????<if?test="user_name!=null">?<!--?這個(gè)標(biāo)簽里的內(nèi)容會交給IfHandler處理?-->??????????????and?--這里的解析與上行的一樣,解析成一個(gè)StaticTextSqlNode??????????????user_name=#{userName}?--這里的解析與上行的一樣,也會被解析成一個(gè)TextSqlNode,并且isDynamic被設(shè)置成true,因?yàn)橛姓嘉环??????????</if><!--?IfHandler會將這里面的內(nèi)個(gè)SqlNode組成MixedSqlNode再組成一個(gè)IfSqlNode?-->??????</select><!--?這五個(gè)SqlNode會被組合成一個(gè)MixedSqlNode?-->?? 附上IfHandler的代碼
[java] view plain copy
private?class?IfHandler?implements?NodeHandler?{??????public?void?handleNode(XNode?nodeToHandle,?List<SqlNode>?targetContents)?{????????//解析子節(jié)點(diǎn)????????List<SqlNode>?contents?=?parseDynamicTags(nodeToHandle);????????//組合????????MixedSqlNode?mixedSqlNode?=?new?MixedSqlNode(contents);????????String?test?=?nodeToHandle.getStringAttribute("test");????????//生成IfSqlNode????????IfSqlNode?ifSqlNode?=?new?IfSqlNode(mixedSqlNode,?test);????????targetContents.add(ifSqlNode);??????}????}?? 其他的nodeHandler在這里就不討論了,實(shí)現(xiàn)方式與IfHandler差不多。如下兩個(gè)方法也不在這里做討論
SqlSource.getBoundSql()方法SqlNode.apply(DynamicContextcontext)方法 ?
http://blog.csdn.net/ashan_li/article/details/50351080
轉(zhuǎn)載于:https://my.oschina.net/xiaominmin/blog/1599100
總結(jié)
以上是生活随笔為你收集整理的Mybatis3源码分析(05)-加载Configuration-加载MappedStatement的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。