<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Mybatis源碼之映射器解析

          共 18107字,需瀏覽 37分鐘

           ·

          2020-12-14 13:46


          點(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個常用的屬性:SQLparameterObjectparameterMappings 這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 parameterMappings;

          它是一個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不變

          傳入多個參數(shù),不帶@Param注解那么MyBatis就會把 parameterObject變?yōu)橐粋€Map

          傳入多個參數(shù),帶@Param注解如果我們使?@Param注解,那么MyBatis就會把parameterObject變?yōu)橐粋€Map

          ?如我們注解 (@Param{"name"} String var1,@Param{"age"} int var2)數(shù)字的鍵值對應(yīng)置換為了name和age

          ??(完)



          掃描二維碼

          加學(xué)習(xí)群

          掃碼加群




          瀏覽 29
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  成年人在线观看网址 | 另类TS人妖一区二区三区 | 97人人操| 婷婷乱伦视频 | 韩国在线免费观看日ccc |