Mybatis源碼之映射器解析

點(diǎn)擊上方藍(lán)字關(guān)注我!

點(diǎn)擊上方藍(lán)字關(guān)注我!
Mybatis映射器
?映射器是MyBatis最強(qiáng)大的?具,也是我們使用MyBatis時?得最多的工具,因此熟 練掌握它?分必要。MyBatis是針對映射?構(gòu)造的SQL構(gòu)建的輕量級框架,并且通過配置 生成對應(yīng)的JavaBean返回給調(diào)用者,?這些配置主要便是映射器,在MyBatis中你可以根 據(jù)情況定義動態(tài)SQL來滿足不同場景的需要,它比其他框架靈活得多。MyBatis還支持?動綁定JavaBean, 我們只要讓SQL返回的字段名和JavaBean 的屬性名保持一致(或者采?駝峰式命名),便可以省掉這些繁瑣的映射配置
?
目錄:
Mybatis映射器
映射器的主要元素
Select元素
insert元素
sql元素
resultMap元素
cache元素
映射器的內(nèi)部組成
我們先再回顧下映射器的主要元素
映射器的主要元素
映射器是由Java接口和XML文件(或注解)共同組成的,Java接口主要定義調(diào)用者接口,XML文件是配置映射器的核心文件,包括以下元素:
select 查詢語句,可以自定義參數(shù),返回結(jié)果集;
insert 插入語句,返回一個整數(shù),表示插入的條數(shù);
update 更新語句,返回一個整數(shù),表示更新的條數(shù);
delete 刪除語句,返回一個整數(shù),表示刪除的條數(shù);
sql 允許定義一部分SQL,然后再各個地方引用;
resultMap 用來描述從數(shù)據(jù)庫結(jié)果集中來加載對象,還可以配置關(guān)聯(lián)關(guān)系,提供映射規(guī)則;
cache 給定命名空間的緩存配置
Select元素
?select元素幫助我們 從數(shù)據(jù)庫中讀出數(shù)據(jù),組裝數(shù)據(jù)給業(yè)務(wù)人員。執(zhí)行select語句前,我們需要定義參數(shù),它 可以是?個簡單的參數(shù)類型,例如int, float , String,也可以是?個復(fù)雜的參數(shù)類型 例如 JavaBean、 Map等,這些都是MyBatis接受的參數(shù)類型。
?
執(zhí)?SQL后,MyBatis也提供了 強(qiáng)?的映射規(guī)則,自動映射來幫助我們把返回的結(jié)果集綁定到JavaBean中。
select 元素的配置眾多,下面簡單說明下:
id: id和Mapper的命名空間組成唯一值,提供給Mybatis調(diào)用,如果不唯一將會報錯
paramterType:傳入的參數(shù)類型,可以是基本類型、map、自定義的java bean;
resultType:返回的結(jié)果類型,可以是基本類型、自定義的java bean;
resultMap:它是最復(fù)雜的元素,可以配置映射規(guī)則、級聯(lián)、typeHandler等,與ResultType不能同時存在;
flushCache:在調(diào)用SQL后,是否要求清空之前查詢的本地緩存和二級緩存,主要用于更新緩存,默認(rèn)為false;
useCache:啟動二級緩存的開關(guān),默認(rèn)只會啟動一級緩存;
timeout:設(shè)置超時參數(shù),等超時的時候?qū)伋霎惓#瑔挝粸槊耄?/p>
fetchSize:獲取記錄的總條數(shù)設(shè)定;
select實(shí)例
需求: ?查詢名稱等于JAVA寶典的用戶
在UserDao中定義接口方法:
User?findIdByName(String?name);
定義UserMapper.xml
<select?id="findIdByName"?parameterType="string"?resultType="User">
????????select
????????u.*
????????from?t_user?u
????????where?u.name=#{name}
select>
?
對操作步驟進(jìn)行歸納概括:
Id標(biāo)出了了這條SQL parameterType定義參數(shù)類型 resuitType定義返回值類型
上面的例子只是傳入單個參數(shù),多個參數(shù)可以使用Map,JavaBean,使用注解方式,等等,下面我們會單獨(dú)介紹.
insert元素
insert屬性和select大部分都相同, 說下3個不同的屬性:
keyProperty:指定哪個列是主鍵,如果是聯(lián)合主鍵可以用逗號隔開;
keyColumn:指定第幾列是主鍵,不能和keyProperty共用;
useGeneratedKeys:是否使用自動增長,默認(rèn)為false;當(dāng)useGeneratedKeys設(shè)為true時,在插入的時候,會回填Java Bean的id值,通過返回的對象可獲取主鍵值。
如果想根據(jù)一些特殊關(guān)系設(shè)置主鍵的值,可以在insert標(biāo)簽內(nèi)使用selectKey標(biāo)簽
<insert?id="insertRole"?useGeneratedKeys="true"?keyProperty="id"?>
????<selectKey?keyProperty="id"?resultType="int"?order="before">
????????select?if(max(id)?is?null,1,max(id)+2)?as?newId?from?t_role????
??selectKey>?
insert>
update和delete 就不單獨(dú)過多介紹了
sql元素
sql元素的意義,在于我們可以定義?串串SQL語句的組成部分,其他的語句可以通過引?來使?它。
?例如,你有一條SQL需要select??個字段映射到JavaBean中去,我的第二 條SQL也是這??個字段映射到JavaBean中去,顯然這些字段寫兩遍不太合適。那么我們 就?sql元素來完成
?
定義:
<sql?id="columns">
????id,
????name,
????remark,
????tid
sql>
使用:
?<select?id="getOne"?resultType="com.liangtengyu.entity.Ttest">
??select??<include?refid="columns"/>??from?t_test?where?id??=?#{id}
?select>
resultMap元素
resultMap是MyBatis里面最復(fù)雜的元素,它的作用是定義映射規(guī)則、級聯(lián)的更新、定制類型轉(zhuǎn)換器等。
由以下元素構(gòu)成:
<resultMap>
????<constructor>?
????????<idArg/>
????????<arg/>
????constructor>
????<id/>?
????<result/>?
????<association/>?
????<collection/>?
????<discriminator>?
????????<case/>
????discriminator>
resultMap>
有的實(shí)體不存在沒有參數(shù)的構(gòu)造方法,需要使用constructor配置有參數(shù)的構(gòu)造方法:
<resultMap?id="role"?type="com.liangtengyu.entity.Role">
????<constructor>
????????<idArg?column="id"?javaType="int"/>
????????<arg?column="role_name"?javaType="string"/>
????constructor>
resultMap>
id指明主鍵列,result配置數(shù)據(jù)庫字段和POJO屬性的映射規(guī)則:
association、collection用于配置級聯(lián)關(guān)系的,分別為一對一和一對多,實(shí)際中,多對多關(guān)系的應(yīng)用不多,因?yàn)楸容^復(fù)雜,會用一對多的關(guān)系把它分解為雙向關(guān)系。discriminator用于這樣一種場景:比如我們?nèi)ンw檢,男和女的體檢項目不同,如果讓男生去檢查婦科項目,是不合理的, 通過discriminator可以根據(jù)性別,返回不同的對象。
級聯(lián)關(guān)系的配置比較多,就不在此演示了,可查看文檔進(jìn)行了解。
cache元素
在沒有顯示配置緩存時,只開啟一級緩存,一級緩存是相對于同一個SqlSession而言的,在參數(shù)和SQL完全一樣的情況下,使用同一個SqlSession對象調(diào)用同一個Mapper的方法,只會執(zhí)行一次SQL。如果是不同的SqlSession對象,因?yàn)椴煌琒qlSession是相互隔離的,即使用相同的Mapper、參數(shù)和方法,還是會再次發(fā)送SQL到數(shù)據(jù)庫去執(zhí)行。
二級緩存是SqlSessionFactory層面上的,需要進(jìn)行顯示配置
這樣很多設(shè)置是默認(rèn)的,有如下屬性可以配置:
eviction:代表緩存回收策略,可選值有LRU最少使用、FIFO先進(jìn)先出、SOFT軟引用,WEAK弱引用;
flushInterval:刷新間隔時間,單位為毫秒,如果不配置,當(dāng)SQL被執(zhí)行時才會刷新緩存;
size:引用數(shù)目,代表緩存最多可以存儲多少對象,不宜設(shè)置過大,設(shè)置過大會導(dǎo)致內(nèi)存溢出;
readOnly:只讀,意味著緩存數(shù)據(jù)只能讀取不能修改;
?在大型服務(wù)器上,可能會使用專用的緩存服務(wù)器,比如Redis緩存,可以通過實(shí)現(xiàn)org.apache.ibatis.cache.Cache接口很方便的實(shí)現(xiàn):
?
public?interface?Cache?{????
????String?getId();?//緩存編號
????void?putObject(Object?var1,?Object?var2);?//保存對象
????Object?getObject(Object?var1);?//獲取對象
????Object?removeObject(Object?var1);?//移除對象
????void?clear();?//清空緩存
????int?getSize();?//獲取緩存對象大小
????ReadWriteLock?getReadWriteLock();?//獲取緩存的讀寫鎖
????}
映射器的內(nèi)部組成
一般而言,一個映射器是由3個部分組成:
?打開Mybatis源碼,在mapping包中可以找到他們
?

MappedStatement,它保存映射器的一個節(jié)點(diǎn)(select|insert|delete|update)并且包括許多我們配置的sql,sql的id、緩存信息,resultMap,parameterType、resultType、languageDriver等重要的配置內(nèi)容。SqlSource,它是提供BoundSql對象的地方,它是MappedStatement的一個屬性。它的主要作用是根據(jù)參數(shù)和其他規(guī)則組裝sql。BoundSql,它是建立SQL和參數(shù)的地方,他有3個常用的屬性:SQL、parameterObject、parameterMappings這3個等會介紹.
idea生成的依賴圖

MappedStatement創(chuàng)建過程
首先我們介紹一下MappedStatement
在SqlSessionFactoryBuilder.build()方法,它會幫我們創(chuàng)建一個SqlSessionFactory,它的build方法:
?public?SqlSessionFactory?build(InputStream?inputStream,?String?environment,?Properties?properties)?{
????try?{
??????XMLConfigBuilder?parser?=?new?XMLConfigBuilder(inputStream,?environment,?properties);
??????return?build(parser.parse());//此處使用解析XML返回的數(shù)據(jù)構(gòu)建?SqlSessionFactory?跟進(jìn)去..
????}?catch?(Exception?e)?{
??????throw?ExceptionFactory.wrapException("Error?building?SqlSession.",?e);
????}?finally?{
??????ErrorContext.instance().reset();
??????try?{
????????inputStream.close();
??????}?catch?(IOException?e)?{
????????//?Intentionally?ignore.?Prefer?previous?error.
??????}
????}
??}
進(jìn)入到parser.parse()方法看看:
?public?Configuration?parse()?{
????if?(parsed)?{?//如果一個xml文件解析兩次?就會報錯?否則才去解析
??????throw?new?BuilderException("Each?XMLConfigBuilder?can?only?be?used?once.");
????}
????parsed?=?true;
????parseConfiguration(parser.evalNode("/configuration"));//解析節(jié)點(diǎn)configuration,跟進(jìn)去...
????return?configuration;
??}
跟進(jìn)代碼parseConfiguration(parser.evalNode("/configuration"));
?private?void?parseConfiguration(XNode?root)?{
????try?{
??????propertiesElement(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);
??????environmentsElement(root.evalNode("environments"));
??????databaseIdProviderElement(root.evalNode("databaseIdProvider"));
??????typeHandlerElement(root.evalNode("typeHandlers"));
??????mapperElement(root.evalNode("mappers"));//解析了很多,但是今天我們的主角是這個..跟進(jìn)去.
????}?catch?(Exception?e)?{
??????throw?new?BuilderException("Error?parsing?SQL?Mapper?Configuration.?Cause:?"?+?e,?e);
????}
??}
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");//獲取三種方式指定的路徑?進(jìn)行判斷?告訴MyBatis?到哪里去找映射文件
??????????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?mapperParser?=?new?XMLMapperBuilder(inputStream,?configuration,?resource,?configuration.getSqlFragments());
????????????mapperParser.parse();//調(diào)用?mapperParser.parse();解析xml??跟進(jìn)去...
??????????}?else?if?(resource?==?null?&&?url?!=?null?&&?mapperClass?==?null)?{
????????????ErrorContext.instance().resource(url);
????????????InputStream?inputStream?=?Resources.getUrlAsStream(url);
????????????XMLMapperBuilder?mapperParser?=?new?XMLMapperBuilder(inputStream,?configuration,?url,?configuration.getSqlFragments());
????????????mapperParser.parse();//調(diào)用?mapperParser.parse();解析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.");
??????????}
????????}
??????}
????}
??}
可以看到在解析mapper標(biāo)簽
??public?void?parse()?{
????if?(!configuration.isResourceLoaded(resource))?{
??????configurationElement(parser.evalNode("/mapper"));//解析xml的mapper標(biāo)簽,并返回調(diào)用configurationElement()方法
??????configuration.addLoadedResource(resource);
??????bindMapperForNamespace();
????}
????parsePendingResultMaps();//缺少資源的再嘗試一下.具體邏輯我們不深入走這里了,有興趣的可以看看源碼.
????parsePendingCacheRefs();
????parsePendingStatements();
??}
configurationElement()方法
??private?void?configurationElement(XNode?context)?{
????try?{
??????String?namespace?=?context.getStringAttribute("namespace");
??????if?(namespace?==?null?||?namespace.isEmpty())?{
????????throw?new?BuilderException("Mapper's?namespace?cannot?be?empty");
??????}
??????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("Error?parsing?Mapper?XML.?The?XML?location?is?'"?+?resource?+?"'.?Cause:?"?+?e,?e);
????}
??}
buildStatementFromContext()方法:
??private?void?buildStatementFromContext(List?list,?String?requiredDatabaseId) ?{
????for?(XNode?context?:?list)?{
??????final?XMLStatementBuilder?statementParser?=?new?XMLStatementBuilder(configuration,?builderAssistant,?context,?requiredDatabaseId);
??????try?{
????????statementParser.parseStatementNode();//會調(diào)用此處,繼續(xù)跟進(jìn)
??????}?catch?(IncompleteElementException?e)?{
????????configuration.addIncompleteStatement(statementParser);
??????}
????}
??}
statementParser.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?parsing
????XMLIncludeTransformer?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:??and??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?=?langDriver.createSqlSource(configuration,?context,?parameterTypeClass);
????//SqlSource是在這里創(chuàng)建的?記住這里等會我們再來分析她.先繼續(xù)走完流程.
????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");
????//對各種屬性進(jìn)行了解析并且調(diào)用addMappedStatement方法.
????builderAssistant.addMappedStatement(id,?sqlSource,?statementType,?sqlCommandType,?
????????fetchSize,?timeout,?parameterMap,?parameterTypeClass,?resultMap,?resultTypeClass,
????????resultSetTypeEnum,?flushCache,?useCache,?resultOrdered,
????????keyGenerator,?keyProperty,?keyColumn,?databaseId,?langDriver,?resultSets);
??}
addMappedStatement()方法:
public?MappedStatement?addMappedStatement(
??????String?id,
??????SqlSource?sqlSource,
??????StatementType?statementType,
??????SqlCommandType?sqlCommandType,
??????Integer?fetchSize,
??????Integer?timeout,
??????String?parameterMap,
??????Class>?parameterType,
??????String?resultMap,
??????Class>?resultType,
??????ResultSetType?resultSetType,
??????boolean?flushCache,
??????boolean?useCache,
??????boolean?resultOrdered,
??????KeyGenerator?keyGenerator,
??????String?keyProperty,
??????String?keyColumn,
??????String?databaseId,
??????LanguageDriver?lang,
??????String?resultSets)?{
????if?(unresolvedCacheRef)?{
??????throw?new?IncompleteElementException("Cache-ref?not?yet?resolved");
????}
????id?=?applyCurrentNamespace(id,?false);
????boolean?isSelect?=?sqlCommandType?==?SqlCommandType.SELECT;
????MappedStatement.Builder?statementBuilder?=?new?MappedStatement.Builder(configuration,?id,?sqlSource,?sqlCommandType)
????????.resource(resource)
????????.fetchSize(fetchSize)
????????.timeout(timeout)
????????.statementType(statementType)
????????.keyGenerator(keyGenerator)
????????.keyProperty(keyProperty)
????????.keyColumn(keyColumn)
????????.databaseId(databaseId)
????????.lang(lang)
????????.resultOrdered(resultOrdered)
????????.resultSets(resultSets)
????????.resultMaps(getStatementResultMaps(resultMap,?resultType,?id))
????????.resultSetType(resultSetType)
????????.flushCacheRequired(valueOrDefault(flushCache,?!isSelect))
????????.useCache(valueOrDefault(useCache,?isSelect))
????????.cache(currentCache);
????ParameterMap?statementParameterMap?=?getStatementParameterMap(parameterMap,?parameterType,?id);
????if?(statementParameterMap?!=?null)?{
??????statementBuilder.parameterMap(statementParameterMap);
????}
????MappedStatement?statement?=?statementBuilder.build();
????configuration.addMappedStatement(statement);//configuration內(nèi)部放入了一個Map
????return?statement;//最終返回MappedStatement
??}
至此,MappedStatement創(chuàng)建完畢
SqlSource的創(chuàng)建過程
回到parseStatementNode()方法找到:
SqlSource?sqlSource?=?langDriver.createSqlSource(configuration,?context,?parameterTypeClass);
createSqlSource方法是LanguageDriver接口的一個方法 她有4個實(shí)現(xiàn)
我們看一下xml實(shí)現(xiàn)
public?class?XMLLanguageDriver?implements?LanguageDriver?{
??@Override
??public?ParameterHandler?createParameterHandler(MappedStatement?mappedStatement,?Object?parameterObject,?BoundSql?boundSql)?{
????return?new?DefaultParameterHandler(mappedStatement,?parameterObject,?boundSql);
??}
??@Override
??public?SqlSource?createSqlSource(Configuration?configuration,?XNode?script,?Class>?parameterType)?{
????XMLScriptBuilder?builder?=?new?XMLScriptBuilder(configuration,?script,?parameterType);
????return?builder.parseScriptNode();//進(jìn)行解析,我們跟進(jìn)..
??}
??....
}
??public?SqlSource?parseScriptNode()?{
????MixedSqlNode?rootSqlNode?=?parseDynamicTags(context);//對動態(tài)標(biāo)簽的處理
????SqlSource?sqlSource;//初始化Null
????if?(isDynamic)?{//判斷是否動態(tài),來調(diào)用不同的SqlSource實(shí)例.
??????sqlSource?=?new?DynamicSqlSource(configuration,?rootSqlNode);
????}?else?{//跟進(jìn)RawSqlSource()
??????sqlSource?=?new?RawSqlSource(configuration,?rootSqlNode,?parameterType);
????}
????return?sqlSource;
??}
??public?RawSqlSource(Configuration?configuration,?String?sql,?Class>?parameterType)?{
????SqlSourceBuilder?sqlSourceParser?=?new?SqlSourceBuilder(configuration);
????Class>?clazz?=?parameterType?==?null???Object.class?:?parameterType;
????sqlSource?=?sqlSourceParser.parse(sql,?clazz,?new?HashMap<>());
??}
sqlSourceParser.parse()方法要對sql進(jìn)行處理,這里我們打個斷點(diǎn),可以看到原始參數(shù)sql為
insert into t_test set context = #{context}

parse方法處理完畢返回:insert into t_test set context = ?并且調(diào)用了StaticSqlSource的構(gòu)造方法
StaticSqlSource類繼實(shí)現(xiàn)了SqlSource接口,
public?class?StaticSqlSource?implements?SqlSource?{
??private?final?String?sql;
??private?final?List?parameterMappings;
??private?final?Configuration?configuration;
??public?StaticSqlSource(Configuration?configuration,?String?sql)?{
????this(configuration,?sql,?null);
??}
??public?StaticSqlSource(Configuration?configuration,?String?sql,?List?parameterMappings) ?{
????this.sql?=?sql;
????this.parameterMappings?=?parameterMappings;
????this.configuration?=?configuration;
??}
??@Override
??public?BoundSql?getBoundSql(Object?parameterObject)?{//這個方法是不是眼熟,BoundSql的構(gòu)建
????return?new?BoundSql(configuration,?sql,?parameterMappings,?parameterObject);
??}
}
至此,SqlSource創(chuàng)建完畢.
BoundSql
public?class?BoundSql?{
??private?final?String?sql;
??private?final?List?parameterMappings;
??private?final?Object?parameterObject;
??private?final?Map?additionalParameters;
??private?final?MetaObject?metaParameters;
........
}
創(chuàng)建SqlSource時,就把參數(shù)放入BoundSql類中來構(gòu)建一個new BoundSql
public?BoundSql?getBoundSql(Object?parameterObject)?{
????return?new?BoundSql(configuration,?sql,?parameterMappings,?parameterObject);
??}
configuration, sql, parameterMappings, parameterObject這些參數(shù)是非常重要的我們逐個分析下
configuration
這個不用多說了吧 如果不認(rèn)識她,那直接打回新手村
sql
這個屬性保存的是我們在映射文件xml中寫的sql語句
例子就是剛剛解析出來的Sql語句:insert into t_test set context = ?
parameterMappings
List
它是一個list,每一個元素都是parameterMapping對象,這個對象會描述包括屬性、名稱、表達(dá)式、javaType、jdbcType、typeHandler等重要信息。
ParameterMapping
public?class?ParameterMapping?{
??private?Configuration?configuration;
??private?String?property;
??private?ParameterMode?mode;
??private?Class>?javaType?=?Object.class;
??private?JdbcType?jdbcType;
??private?Integer?numericScale;
??private?TypeHandler>?typeHandler;
??private?String?resultMapId;
??private?String?jdbcTypeName;
??private?String?expression;
??......
??}
parameterObject
parameterObject是參數(shù),可以傳遞簡單對象,pojo、map或者@param注解的參數(shù)
傳入簡單對象 (包括int、 float、 double等),?如當(dāng)我們傳遞int類型時,MyBatis
會把參數(shù)包裝成為Integer對象傳遞,類似的long、 float、 double也是如此.
傳入pojo、map那么這個parameterObject參數(shù)就是你傳入的POJO或者M(jìn)ap不變
?如我們注解
(@Param{"name"} String var1,@Param{"age"} int var2)數(shù)字的鍵值對應(yīng)置換為了name和age傳入多個參數(shù),不帶@Param注解那么MyBatis就會把
parameterObject變?yōu)橐粋€Map傳入多個參數(shù),帶@Param注解如果我們使?@Param注解,那么MyBatis就會把parameterObject變?yōu)橐粋€Map
??(完)
掃描二維碼
加學(xué)習(xí)群
掃碼加群

