日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

Mybatis 高级结果映射 ResultMap Association Collection

發(fā)布時(shí)間:2023/12/15 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Mybatis 高级结果映射 ResultMap Association Collection 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

來(lái)源:http://www.verydemo.com/demo_c152_i1880.html

MyBatis的創(chuàng)建基于這樣一個(gè)思想:數(shù)據(jù)庫(kù)并不是您想怎樣就怎樣的。雖然我們希望所有的數(shù)據(jù)庫(kù)遵守第三范式或BCNF(修正的第三范式),但它們不是。如果有一個(gè)數(shù)據(jù)庫(kù)能夠完美映射到所有應(yīng)用程序,也將是非常棒的,但也沒(méi)有。結(jié)果映射就是MyBatis為解決這些問(wèn)題而提供的解決方案。例如,我們?nèi)绾?strong>映射下面這條語(yǔ)句?

查看文本打印?
  • <!--?Very?Complex?Statement?-->??
  • <select?id="selectBlogDetails"?parameterType="int"?resultMap="detailedBlogResultMap">??
  • select??
  • B.id?as?blog_id,??
  • B.title?as?blog_title,??
  • B.author_id?as?blog_author_id,??
  • A.id?as?author_id,??
  • A.username?as?author_username,??
  • A.password?as?author_password,??
  • A.email?as?author_email,??
  • A.bio?as?author_bio,??
  • A.favourite_section?as?author_favourite_section,??
  • P.id?as?post_id,??
  • P.blog_id?as?post_blog_id,??
  • P.author_id?as?post_author_id,??
  • P.created_on?as?post_created_on,??
  • P.section?as?post_section,??
  • P.subject?as?post_subject,??
  • P.draft?as?draft,??
  • P.body?as?post_body,??
  • C.id?as?comment_id,??
  • C.post_id?as?comment_post_id,??
  • C.name?as?comment_name,??
  • C.comment?as?comment_text,??
  • T.id?as?tag_id,??
  • T.name?as?tag_name??
  • from?Blog?B??
  • left?outer?join?Author?A?on?B.author_id?=?A.id??
  • left?outer?join?Post?P?on?B.id?=?P.blog_id??
  • left?outer?join?Comment?C?on?P.id?=?C.post_id??
  • left?outer?join?Post_Tag?PT?on?PT.post_id?=?P.id??
  • left?outer?join?Tag?T?on?PT.tag_id?=?T.id??
  • where?B.id?=?#{id}??
  • </select>??
  • <wbr>??
  • 您可能想要把它映射到一個(gè)智能的對(duì)象模型,包括由一個(gè)作者寫的一個(gè)博客,有許多文章(Post,帖子),每個(gè)文章由0個(gè)或者多個(gè)評(píng)論和標(biāo)簽。下面是一個(gè)復(fù)雜ResultMap?的完整例子(假定作者、博客、文章、評(píng)論和標(biāo)簽都是別名)。仔細(xì)看看這個(gè)例子,但是不用太擔(dān)心,我們會(huì)一步步地來(lái)分析,一眼看上去可能讓人沮喪,但是實(shí)際上非常簡(jiǎn)單的

    查看文本打印?
  • <!--?Very?Complex?Result?Map?-->??
  • <resultMap?id="detailedBlogResultMap"?type="Blog">??
  • <constructor>??
  • <idArg?column="blog_id"?javaType="int"/>??
  • </constructor>??
  • <result?property="title"?column="blog_title"/>??
  • <association?property="author"?column="blog_author_id"?javaType="?Author">??
  • <id?property="id"?column="author_id"/>??
  • <result?property="username"?column="author_username"/>??
  • <result?property="password"?column="author_password"/>??
  • <result?property="email"?column="author_email"/>??
  • <result?property="bio"?column="author_bio"/>??
  • <result?property="favouriteSection"?column="author_favourite_section"/>??
  • </association>??
  • <collection?property="posts"?ofType="Post">??
  • <id?property="id"?column="post_id"/>??
  • <result?property="subject"?column="post_subject"/>??
  • <association?property="author"?column="post_author_id"?javaType="Author"/>??
  • <collection?property="comments"?column="post_id"?ofType="?Comment">??
  • <id?property="id"?column="comment_id"/>??
  • </collection>??
  • <collection?property="tags"?column="post_id"?ofType="?Tag"?>??
  • <id?property="id"?column="tag_id"/>??
  • </collection>??
  • <discriminator?javaType="int"?column="draft">??
  • <case?value="1"?resultType="DraftPost"/>??
  • </discriminator>??
  • </collection>??
  • </resultMap>?<wbr>??
  • 這個(gè)resultMap?的元素的子元素比較多,討論起來(lái)比較寬泛。下面我們從概念上概覽一下這個(gè)resultMap的元素。

    ?

    resultMap

    ·constructor實(shí)例化的時(shí)候通過(guò)構(gòu)造器將結(jié)果集注入到類中

    oidArg– ID?參數(shù);?將結(jié)果集標(biāo)記為ID,以方便全局調(diào)用

    oarg注入構(gòu)造器的結(jié)果

    ·id結(jié)果ID,將結(jié)果集標(biāo)記為ID,以方便全局調(diào)用

    ·result注入一個(gè)字段或者javabean屬性的結(jié)果

    ·association復(fù)雜類型聯(lián)合;許多查詢結(jié)果合成這個(gè)類型

    o嵌套結(jié)果映射– associations能引用自身,或者從其它地方引用

    ·collection復(fù)雜類型集合

    o嵌套結(jié)果映射– collections能引用自身,或者從其它地方引用

    ·discriminator使用一個(gè)結(jié)果值以決定使用哪個(gè)resultMap

    ocase基于不同值的結(jié)果映射

    §嵌套結(jié)果映射–case也能引用它自身,?所以也能包含這些同樣的元素。它也可以從外部引用resultMap

    ?

    è最佳實(shí)踐:逐步地生成resultMap,單元測(cè)試對(duì)此非常有幫助。如果您嘗試一下子就生成像上面這樣巨大的resultMap,可能會(huì)出錯(cuò),并且工作起來(lái)非常吃力。從簡(jiǎn)單地開(kāi)始,再一步步地?cái)U(kuò)展,并且進(jìn)行單元測(cè)試。使用框架開(kāi)發(fā)有一個(gè)缺點(diǎn),它們有時(shí)像是一個(gè)黑合。為了確保達(dá)到您所預(yù)想的行為,最好的方式就是進(jìn)行單元測(cè)試。這對(duì)提交bugs?也非常有用。

    ?

    下一節(jié),我們一步步地查看這些細(xì)節(jié)。

    id, result元素

    <id property="id" column="post_id"/>

    <result property="subject" column="post_subject"/>

    ?

    這是最基本的結(jié)果映射id?和result?將列映射到屬性或簡(jiǎn)單的數(shù)據(jù)類型字段(String, int, double, Date)

    這兩者唯一不同的是,在比較對(duì)象實(shí)例時(shí)id?作為結(jié)果集的標(biāo)識(shí)屬性。這有助于提高總體性能,特別是應(yīng)用緩存和嵌套結(jié)果映射的時(shí)候。

    ?

    Idresult屬性如下:

    ?

    Attribute

    Description

    property

    映射數(shù)據(jù)庫(kù)列的字段或?qū)傩浴H绻?span lang="EN-CA">JavaBean?的屬性與給定的名稱匹配,就會(huì)使用匹配的名字。否則,MyBatis?將搜索給定名稱的字段。兩種情況下您都可以使用逗點(diǎn)的屬性形式。比如,您可以映射到“username”,也可以映射到“address.street.number”

    column

    數(shù)據(jù)庫(kù)的列名或者列標(biāo)簽別名。與傳遞給resultSet.getString(columnName)的參數(shù)名稱相同。

    javaType

    完整java類名或別名(參考上面的內(nèi)置別名列表)。如果映射到一個(gè)JavaBean,那MyBatis?通常會(huì)自行檢測(cè)到。然而,如果映射到一個(gè)HashMap,那您應(yīng)該明確指定javaType?來(lái)確保所需行為。

    jdbcType

    這張表下面支持的JDBC類型列表列出的JDBC類型。這個(gè)屬性只在insertupdatedelete?的時(shí)候針對(duì)允許空的列有用。JDBC?需要這項(xiàng),但MyBatis?不需要。如果您直接編寫JDBC代碼,在允許為空值的情況下需要指定這個(gè)類型。

    typeHandler

    我們已經(jīng)在文檔中討論過(guò)默認(rèn)類型處理器。使用這個(gè)屬性可以重寫默認(rèn)類型處理器。它的值可以是一個(gè)TypeHandler實(shí)現(xiàn)的完整類名,也可以是一個(gè)類型別名。

    支持的JDBC類型

    MyBatis支持如下的JDBC類型:

    BIT

    FLOAT

    CHAR

    TIMESTAMP

    OTHER

    UNDEFINED

    TINYINT

    REAL

    VARCHAR

    BINARY

    BLOB

    NVARCHAR

    SMALLINT

    DOUBLE

    LONGVARCHAR

    VARBINARY

    CLOB

    NCHAR

    INTEGER

    NUMERIC

    DATE

    LONGVARBINARY

    BOOLEAN

    NCLOB

    BIGINT

    DECIMAL

    TIME

    NULL

    CURSOR

    ?

    ?

    Constructor元素

    <constructor>

    <idArg column="id" javaType="int"/>

    <arg column=”username” javaType=”String”/>

    </constructor>

    ?

    當(dāng)屬性與DTO,或者與您自己的域模型一起工作的時(shí)候,許多場(chǎng)合要用到不變類。通常,包含引用,或者查找的數(shù)據(jù)很少或者數(shù)據(jù)不會(huì)改變的的表,適合映射到不變類中。構(gòu)造器注入允許您在類實(shí)例化后給類設(shè)值,這不需要通過(guò)public方法。MyBatis同樣也支持private屬性和JavaBeans的私有屬性達(dá)到這一點(diǎn),但是一些用戶可能更喜歡使用構(gòu)造器注入。構(gòu)造器元素可以做到這點(diǎn)。

    ?

    考慮下面的構(gòu)造器:

    ?

    public class User {

    //…

    public User(int id, String username) {

    //…

    }

    //…

    }

    ?

    為了將結(jié)果注入構(gòu)造器,MyBatis需要使用它的參數(shù)類型來(lái)標(biāo)記構(gòu)造器。Java沒(méi)有辦法通過(guò)參數(shù)名稱來(lái)反射獲得。因此當(dāng)創(chuàng)建constructor?元素,確保參數(shù)是按順序的并且指定了正確的類型。

    ?

    <constructor>

    <idArg column="id" javaType="int"/>

    <arg column=”username” javaType=”String”/>

    </constructor>

    ?

    其它的屬性與規(guī)則與idresult元素的一樣。

    ?

    Attribute

    Description

    column

    數(shù)據(jù)庫(kù)的列名或者列標(biāo)簽別名。與傳遞給resultSet.getString(columnName)的參數(shù)名稱相同。

    javaType

    完整java類名或別名(參考上面的內(nèi)置別名列表)。如果映射到一個(gè)JavaBean,那MyBatis?通常會(huì)自行檢測(cè)到。然而,如果映射到一個(gè)HashMap,那您應(yīng)該明確指定javaType?來(lái)確保所需行為。

    jdbcType

    支持的JDBC類型列表中列出的JDBC類型。這個(gè)屬性只在insert,update?或delete?的時(shí)候針對(duì)允許空的列有用。JDBC?需要這項(xiàng),但MyBatis?不需要。如果您直接編寫JDBC代碼,在允許為空值的情況下需要指定這個(gè)類型。

    typeHandler

    我們已經(jīng)在文檔中討論過(guò)默認(rèn)類型處理器。使用這個(gè)屬性可以重寫默認(rèn)類型處理器。它的值可以是一個(gè)TypeHandler實(shí)現(xiàn)的完整類名,也可以是一個(gè)類型別名。

    Association元素

    <association property="author" column="blog_author_id" javaType=" Author">

    <id property="id" column="author_id"/>

    <result property="username" column="author_username"/>

    </association>

    Association元素處理“has-one”(一對(duì)一)這種類型關(guān)系。比如在我們的例子中,一個(gè)Blog有一個(gè)Author。聯(lián)合映射與其它的結(jié)果映射工作方式差不多,指定propertycolumnjavaType(通常MyBatis會(huì)自動(dòng)識(shí)別)、jdbcType(如果需要)、typeHandler

    不同的地方是您需要告訴MyBatis?如何加載一個(gè)聯(lián)合查詢。MyBatis使用兩種方式來(lái)加載:

    ·Nested Select:通過(guò)執(zhí)行另一個(gè)返回預(yù)期復(fù)雜類型的映射SQL語(yǔ)句(即引用外部定義好的SQL語(yǔ)句塊)。

    ·Nested Results:通過(guò)嵌套結(jié)果映射nested result mappings)來(lái)處理聯(lián)接結(jié)果集(joined results)的重復(fù)子集。

    首先,讓我們檢查一下元素屬性。正如您看到的,它不同于普通只有selectresultMap屬性的結(jié)果映射

    Attribute

    Description

    property

    映射數(shù)據(jù)庫(kù)列的字段或?qū)傩浴H绻?span lang="EN-CA">JavaBean?的屬性與給定的名稱匹配,就會(huì)使用匹配的名字。否則,MyBatis?將搜索給定名稱的字段。兩種情況下您都可以使用逗點(diǎn)的屬性形式。比如,您可以映射”username”,也可以映射到更復(fù)雜點(diǎn)的”address.street.number”

    column

    數(shù)據(jù)庫(kù)的列名或者列標(biāo)簽別名。與傳遞給resultSet.getString(columnName)的參數(shù)名稱相同。

    注意: 在處理組合鍵時(shí),您可以使用column= “{prop1=col1,prop2=col2}”這樣的語(yǔ)法,設(shè)置多個(gè)列名傳入到嵌套查詢語(yǔ)句。這就會(huì)把prop1prop2設(shè)置到目標(biāo)嵌套選擇語(yǔ)句的參數(shù)對(duì)象中。

    javaType

    完整java類名或別名(參考上面的內(nèi)置別名列表)。如果映射到一個(gè)JavaBean,那MyBatis?通常會(huì)自行檢測(cè)到。然而,如果映射到一個(gè)HashMap,那您應(yīng)該明確指定javaType?來(lái)確保所需行為。

    jdbcType

    支持的JDBC類型列表中列出的JDBC類型。這個(gè)屬性只在insert,update?或delete?的時(shí)候針對(duì)允許空的列有用。JDBC?需要這項(xiàng),但MyBatis?不需要。如果您直接編寫JDBC代碼,在允許為空值的情況下需要指定這個(gè)類型。

    typeHandler

    我們已經(jīng)在文檔中討論過(guò)默認(rèn)類型處理器。使用這個(gè)屬性可以重寫默認(rèn)類型處理器。它的值可以是一個(gè)TypeHandler實(shí)現(xiàn)的完整類名,也可以是一個(gè)類型別名。

    聯(lián)合嵌套選擇(Nested Select for Association

    select

    通過(guò)這個(gè)屬性,通過(guò)ID引用另一個(gè)加載復(fù)雜類型的映射語(yǔ)句。從指定列屬性中返回的值,將作為參數(shù)設(shè)置給目標(biāo)select?語(yǔ)句。表格下方將有一個(gè)例子。注意:在處理組合鍵時(shí),您可以使用column={prop1=col1,prop2=col2}”這樣的語(yǔ)法,設(shè)置多個(gè)列名傳入到嵌套語(yǔ)句。這就會(huì)把prop1prop2設(shè)置到目標(biāo)嵌套語(yǔ)句的參數(shù)對(duì)象中。

    ?例如:??

    查看文本打印?
  • <resultMap?id=”blogResult”?type=”Blog”>??
  • <association?property="author"?column="blog_author_id"?javaType="Author"??
  • select=”selectAuthor”/>??
  • </resultMap>??
  • ???
  • <select?id=”selectBlog”?parameterType=”int”?resultMap=”blogResult”>??
  • SELECT?*?FROM?BLOG?WHERE?ID?=?#{id}??
  • </select>??
  • ???
  • <select?id=”selectAuthor”?parameterType=”int”?resultType="Author">??
  • SELECT?*?FROM?AUTHOR?WHERE?ID?=?#{id}??
  • </select>??
  • <wbr>??
  • 我們使用兩個(gè)select語(yǔ)句:一個(gè)用來(lái)加載Blog,另一個(gè)用來(lái)加載AuthorBlogresultMap?描述了使用“selectAuthor”語(yǔ)句來(lái)加載author的屬性。

    如果列名和屬性名稱相匹配的話,所有匹配的屬性都會(huì)自動(dòng)加載。

    ?

    譯者注:

    上面的例子,首先執(zhí)行<select id=selectBlog”>,執(zhí)行結(jié)果存放到<resultMap id=blogResult”>結(jié)果映射中。“blogResult”是一個(gè)Blog類型,從<select id=selectBlog”>查出的數(shù)據(jù)都會(huì)自動(dòng)賦值給”blogResult”的與列名匹配的屬性,這時(shí)blog_idtitle等就被賦值了。同時(shí)“blogResult”還有一個(gè)關(guān)聯(lián)屬性"Author",執(zhí)行嵌套查詢select=”selectAuthor”后,Author對(duì)象的屬性idusernamepasswordemailbio也被賦于數(shù)據(jù)庫(kù)匹配的值。

    ?

    Blog

    {

    blog_id;

    title;

    Author author

    {

    id;

    username;

    password;

    email;

    bio;

    ?

    }

    ?

    }

    ?

    雖然這個(gè)方法簡(jiǎn)單,但是對(duì)于大數(shù)據(jù)集或列表查詢,就不盡如人意了。這個(gè)問(wèn)題被稱為“N+1?選擇問(wèn)題”(N+1 Selects Problem)。概括地說(shuō),N+1選擇問(wèn)題是這樣產(chǎn)生的:

    ·您執(zhí)行單條SQL語(yǔ)句去獲取一個(gè)列表的記錄( “+1”)

    ·對(duì)列表中的每一條記錄,再執(zhí)行一個(gè)聯(lián)合select?語(yǔ)句來(lái)加載每條記錄更加詳細(xì)的信息(“N”)

    這個(gè)問(wèn)題會(huì)導(dǎo)致成千上萬(wàn)的SQL語(yǔ)句的執(zhí)行,因此并非總是可取的。

    上面的例子,MyBatis可以使用延遲加載這些查詢,因此這些查詢立馬可節(jié)省開(kāi)銷。然而,如果您加載一個(gè)列表后立即迭代訪問(wèn)嵌套的數(shù)據(jù),這將會(huì)調(diào)用所有的延遲加載,因此性能會(huì)變得非常糟糕。

    鑒于此,這有另外一種方式。

    聯(lián)合嵌套結(jié)果集(Nested Results for Association)?

    resultMap

    一個(gè)可以映射聯(lián)合嵌套結(jié)果集到一個(gè)適合的對(duì)象視圖上的ResultMap?。這是一個(gè)替代的方式去調(diào)用另一個(gè)select?語(yǔ)句。它允許您去聯(lián)合多個(gè)表到一個(gè)結(jié)果集里。這樣的結(jié)果集可能包括冗余的、重復(fù)的需要分解和正確映射到一個(gè)嵌套對(duì)象視圖的數(shù)據(jù)組。簡(jiǎn)言之,MyBatis?讓您把結(jié)果映射‘鏈接’到一起,用來(lái)處理嵌套結(jié)果。舉個(gè)例子會(huì)更好理解,例子在表格下方。

    您已經(jīng)在上面看到了一個(gè)非常復(fù)雜的嵌套聯(lián)合的例子,接下的演示的例子會(huì)更簡(jiǎn)單一些。我們把BlogAuthor表聯(lián)接起來(lái)查詢,而不是執(zhí)行分開(kāi)的查詢語(yǔ)句:

    查看文本打印?
  • <select?id="selectBlog"?parameterType="int"?resultMap="blogResult">??
  • select??
  • B.id?as?blog_id,??
  • B.title?as?blog_title,??
  • B.author_id?as?blog_author_id,??
  • A.id?as?author_id,??
  • A.username?as?author_username,??
  • A.password?as?author_password,??
  • A.email?as?author_email,??
  • A.bio?as?author_bio??
  • from?Blog?B?left?outer?join?Author?A?on?B.author_id?=?A.id??
  • where?B.id?=?#{id}??
  • </select>?<wbr>??
  • 注意到這個(gè)連接(join),要確保所有的別名都是唯一且無(wú)歧義的。這使映射容易多了,現(xiàn)在我們來(lái)映射結(jié)果集:

    查看文本打印?
  • <resultMap?id="blogResult"?type="Blog">??
  • <id?property=”blog_id”?column="id"?/>??
  • <result?property="title"?column="blog_title"/>??
  • <association?property="author"?column="blog_author_id"?javaType="Author"??
  • resultMap=”authorResult”/>??
  • </resultMap>??
  • ???
  • <resultMap?id="authorResult"?type="Author">??
  • <id?property="id"?column="author_id"/>??
  • <result?property="username"?column="author_username"/>??
  • <result?property="password"?column="author_password"/>??
  • <result?property="email"?column="author_email"/>??
  • <result?property="bio"?column="author_bio"/>??
  • </resultMap>?<wbr>??
  • 在上面的例子中,您會(huì)看到Blog的作者(“author”)聯(lián)合一個(gè)“authorResult”結(jié)果映射來(lái)加載Author實(shí)例。

    重點(diǎn)提示:id元素在嵌套結(jié)果映射中扮演了非常重要的角色,您應(yīng)該總是指定一個(gè)或多個(gè)屬性來(lái)唯一標(biāo)識(shí)這個(gè)結(jié)果集。事實(shí)上,如果您沒(méi)有那樣做,MyBatis也會(huì)工作,但是會(huì)導(dǎo)致嚴(yán)重性能開(kāi)銷。選擇盡量少的屬性來(lái)唯一標(biāo)識(shí)結(jié)果,而使用主鍵是最明顯的選擇(即使是復(fù)合主鍵)。

    上面的例子使用一個(gè)擴(kuò)展的resultMap?元素來(lái)聯(lián)合映射。這可使Author結(jié)果映射可重復(fù)使用。然后,如果您不需要重用它,您可以直接嵌套這個(gè)聯(lián)合結(jié)果映射。下面例子就是使用這樣的方式:?

    查看文本打印?
  • <resultMap?id="blogResult"?type="Blog">??
  • <id?property=”blog_id”?column="id"?/>??
  • <result?property="title"?column="blog_title"/>??
  • <association?property="author"?column="blog_author_id"?javaType="Author">??
  • <id?property="id"?column="author_id"/>??
  • <result?property="username"?column="author_username"/>??
  • <result?property="password"?column="author_password"/>??
  • <result?property="email"?column="author_email"/>??
  • <result?property="bio"?column="author_bio"/>??
  • </association>??
  • </resultMap>?<wbr>??
  • 在上面的例子中您已經(jīng)看到如果處理“一對(duì)一”(“has one”)類型的聯(lián)合查詢。但是對(duì)于“一對(duì)多”(“has many”)的情況如果處理呢?這個(gè)問(wèn)題在下一節(jié)討論。

    Collection元素?

    查看文本打印?
  • <collection?property="posts"?ofType="domain.blog.Post">??
  • <id?property="id"?column="post_id"/>??
  • <result?property="subject"?column="post_subject"/>??
  • <result?property="body"?column="post_body"/>??
  • </collection>??
  • <wbr>??
  • collection元素的作用差不多和association元素的作用一樣。事實(shí)上,它們非常相似,以至于再對(duì)相似點(diǎn)進(jìn)行描述會(huì)顯得冗余,因此我們只關(guān)注它們的不同點(diǎn)。

    繼續(xù)我們上面的例子,一個(gè)Blog只有一個(gè)Author。但一個(gè)Blog有許多帖子(文章)。在Blog類中,會(huì)像下面這樣定義相應(yīng)屬性:?

    private List<Post> posts;

    映射一個(gè)嵌套結(jié)果集到一個(gè)列表,我們使用collection元素。就像association?元素那樣,我們使用嵌套查詢,或者從連接中嵌套結(jié)果集。?

    集合嵌套選擇(Nested Select for Collection

    首先我們使用嵌套選擇來(lái)加載Blog的文章。?

    查看文本打印?
  • <resultMap?id=”blogResult”?type=”Blog”>??
  • <collection?property="posts"?javaType=”ArrayList”?column="blog_id"??
  • ofType="Post"?select=”selectPostsForBlog”/>??
  • </resultMap>??
  • ???
  • <select?id=”selectBlog”?parameterType=”int”?resultMap=”blogResult”>??
  • SELECT?*?FROM?BLOG?WHERE?ID?=?#{id}??
  • </select>??
  • ???
  • <select?id=”selectPostsForBlog”?parameterType=”int”?resultType="Author">??
  • SELECT?*?FROM?POST?WHERE?BLOG_ID?=?#{id}??
  • </select>?<wbr>??
  • 一看上去這有許多東西需要注意,但大部分看起與我們?cè)?span lang="EN-CA">association元素中學(xué)過(guò)的相似。首先,您會(huì)注意到我們使用了collection元素,然后會(huì)注意到一個(gè)新的屬性“ofType”。這個(gè)元素是用來(lái)區(qū)別JavaBean屬性(或者字段)類型和集合所包括的類型。因此您會(huì)讀到下面這段代碼。

    ?

    <collection property="posts" javaType=”ArrayList” column="blog_id"

    ofType="Post" select=”selectPostsForBlog”/>

    è理解為:“一個(gè)名為posts,類型為PostArrayList集合(A collection of posts in an ArrayList of type Post)” 。

    javaType屬性不是必須的,通常MyBatis?會(huì)自動(dòng)識(shí)別,所以您通常可以簡(jiǎn)略地寫成:

    <collection property="posts" column="blog_id" ofType="Post"

    select=”selectPostsForBlog”/>



    集合的嵌套結(jié)果集(Nested Results for Collection

    這時(shí)候,您可能已經(jīng)猜出嵌套結(jié)果集是怎樣工作的了,因?yàn)樗cassociation非常相似,只不過(guò)多了一個(gè)屬性“ofType”

    讓我們看下這個(gè)SQL:?

    查看文本打印?
  • <select?id="selectBlog"?parameterType="int"?resultMap="blogResult">??
  • select??
  • B.id?as?blog_id,??
  • B.title?as?blog_title,??
  • B.author_id?as?blog_author_id,??
  • P.id?as?post_id,??
  • P.subject?as?post_subject,??
  • P.body?as?post_body,??
  • from?Blog?B??
  • left?outer?join?Post?P?on?B.id?=?P.blog_id??
  • where?B.id?=?#{id}??
  • </select>??
  • <wbr>??
  • 同樣,我們把BlogPost兩張表連接在一起,并且也保證列標(biāo)簽名在映射的時(shí)候是唯一且無(wú)歧義的。現(xiàn)在將BlogPost的集合映射在一起是多么簡(jiǎn)單:

    查看文本打印?
  • <resultMap?id="blogResult"?type="Blog">??
  • <id?property=”id”?column="blog_id"?/>??
  • <result?property="title"?column="blog_title"/>??
  • <collection?property="posts"?ofType="Post">??
  • <id?property="id"?column="post_id"/>??
  • <result?property="subject"?column="post_subject"/>??
  • <result?property="body"?column="post_body"/>??
  • </collection>??
  • </resultMap>??
  • 再次強(qiáng)調(diào)一下,id?元素是非常重要的。如果您忘了或者不知道id?元素的作用,請(qǐng)先讀一下上面association?一節(jié)。

    如果希望結(jié)果映射有更好的可重用性,您可以使用下面的方式:

    查看文本打印?
  • <resultMap?id="blogResult"?type="Blog">??
  • <id?property=”id”?column="blog_id"?/>??
  • <result?property="title"?column="blog_title"/>??
  • <collection?property="posts"?ofType="Post"?resultMap=”blogPostResult”/>??
  • </resultMap>??
  • ???
  • <resultMap?id="blogPostResult"?type="Post">??
  • <id?property="id"?column="post_id"/>??
  • <result?property="subject"?column="post_subject"/>??
  • <result?property="body"?column="post_body"/>??
  • </resultMap>??
  • èNote:在您的映射中沒(méi)有深度、寬度、聯(lián)合和集合數(shù)目的限制。但應(yīng)該謹(jǐn)記,在進(jìn)行映射的時(shí)候也要考慮性能的因素。應(yīng)用程序的單元測(cè)試和性能測(cè)試幫助您發(fā)現(xiàn)最好的方式可能要花很長(zhǎng)時(shí)間。但幸運(yùn)的是,MyBatis允許您以后可以修改您的想法,這時(shí)只需要修改少量代碼就行了。

    關(guān)于高級(jí)聯(lián)合和集合映射是一個(gè)比較深入的課題,文檔只能幫您了解到這里,多做一些實(shí)踐,一切將很快變得容易理解。

    Discriminator元素

    <discriminator javaType="int" column="draft">

    <case value="1" resultType="DraftPost"/>

    </discriminator>

    ?

    有時(shí)候一條數(shù)據(jù)庫(kù)查詢可能會(huì)返回包括各種不同的數(shù)據(jù)類型的結(jié)果集。Discriminator(識(shí)別器)元素被設(shè)計(jì)來(lái)處理這種情況,以及其它像類繼承層次情況。識(shí)別器非常好理解,它就像java里的switch語(yǔ)句。

    ?

    Discriminator定義要指定columnjavaType屬性。列是MyBatis將要取出進(jìn)行比較的值,javaType用來(lái)確定適當(dāng)?shù)臏y(cè)試是否正確運(yùn)行(雖然String在大部分情況下都可以工作),例:??

    查看文本打印?
  • <resultMap?id="vehicleResult"?type="Vehicle">??
  • <id?property=”id”?column="id"?/>??
  • <result?property="vin"?column="vin"/>??
  • <result?property="year"?column="year"/>??
  • <result?property="make"?column="make"/>??
  • <result?property="model"?column="model"/>??
  • <result?property="color"?column="color"/>??
  • <discriminator?javaType="int"?column="vehicle_type">??
  • <case?value="1"?resultMap="carResult"/>??
  • <case?value="2"?resultMap="truckResult"/>??
  • <case?value="3"?resultMap="vanResult"/>??
  • <case?value="4"?resultMap="suvResult"/>??
  • </discriminator>??
  • </resultMap>??
  • <wbr>??
  • 在這個(gè)例子中,MyBatis將會(huì)從結(jié)果集中取出每條記錄,然后比較它的vehicle type的值。如果匹配任何discriminator中的case,它將使用由case指定的resultMap。這是排它性的,換句話說(shuō),其它的caseresultMap將會(huì)被忽略(除非使用我們下面說(shuō)到的extended)。如果沒(méi)有匹配到任何caseMyBatis只是簡(jiǎn)單的使用定義在discriminator塊外面的resultMap。所以,如果carResult像下面這樣定義:

    ?

    <resultMap id="carResult" type="Car">

    <result property=”doorCount” column="door_count" />

    </resultMap>

    ?

    那么,只有doorCount屬性會(huì)被加載。這樣做是為了與識(shí)別器cases群組完全獨(dú)立開(kāi)來(lái),哪怕它與上一層的resultMap?一點(diǎn)關(guān)系都沒(méi)有。在剛才的例子里我們當(dāng)然知道carsvehicles的關(guān)系,a Car is-a Vehicle。因此,我們也要把其它屬性加載進(jìn)來(lái)。我們要稍稍改動(dòng)一下resultMap

    ?

    <resultMap id="carResult" type="Car"extends=”vehicleResult”>

    <result property=”doorCount” column="door_count" />

    </resultMap>

    ?

    現(xiàn)在,vehicleResultcarResult的所有屬性都會(huì)被加載。

    可能有人會(huì)認(rèn)為這樣擴(kuò)展映射定義有一點(diǎn)單調(diào)了,所以還有一種可選的更加簡(jiǎn)單明了的映射風(fēng)格語(yǔ)法。例如:

    查看文本打印?
  • <resultMap?id="vehicleResult"?type="Vehicle">??
  • <id?property=”id”?column="id"?/>??
  • <result?property="vin"?column="vin"/>??
  • <result?property="year"?column="year"/>??
  • <result?property="make"?column="make"/>??
  • <result?property="model"?column="model"/>??
  • <result?property="color"?column="color"/>??
  • <discriminator?javaType="int"?column="vehicle_type">??
  • <case?value="1"?resultType="carResult">??
  • <result?property=”doorCount”?column="door_count"?/>??
  • </case>??
  • <case?value="2"?resultType="truckResult">??
  • <result?property=”boxSize”?column="box_size"?/>??
  • <result?property=”extendedCab”?column="extended_cab"?/>??
  • </case>??
  • <case?value="3"?resultType="vanResult">??
  • <result?property=”powerSlidingDoor”?column="power_sliding_door"?/>??
  • </case>??
  • <case?value="4"?resultType="suvResult">??
  • <result?property=”allWheelDrive”?column="all_wheel_drive"?/>??
  • </case>??
  • </discriminator>??
  • </resultMap>?<wbr>??
  • è記住:對(duì)于這么多的結(jié)果映射,如果您不指定任何的結(jié)果集,那么MyBatis?會(huì)自動(dòng)地將列名與屬性相匹配。所以上面所舉的例子比實(shí)際中需要的要詳細(xì)。盡管如此,大部分?jǐn)?shù)據(jù)庫(kù)有點(diǎn)復(fù)雜,并且它并不是所有情況都是完全可以適用的。

    Cache元素

    MyBatis包含一個(gè)強(qiáng)大的、可配置、可定制的查詢緩存機(jī)制。MyBatis 3?的緩存實(shí)現(xiàn)有了許多改進(jìn),使它更強(qiáng)大更容易配置。默認(rèn)的情況,緩存是沒(méi)有開(kāi)啟,除了會(huì)話緩存以外,它可以提高性能,且能解決循環(huán)依賴。開(kāi)啟二級(jí)緩存,您只需要在SQL映射文件中加入簡(jiǎn)單的一行:

    ?

    <cache/>

    ?

    這句簡(jiǎn)單的語(yǔ)句作用如下:

    ·所有映射文件里的select語(yǔ)句的結(jié)果都會(huì)被緩存。

    ·所有映射文件里的insertupdatedelete語(yǔ)句執(zhí)行都會(huì)清空緩存。

    ·緩存使用最近最少使用算法(LRU)來(lái)回收

    ·緩存不會(huì)被設(shè)定的時(shí)間所清空。

    ·每個(gè)緩存可以存儲(chǔ)1024?個(gè)列表或?qū)ο蟮囊?#xff08;不管查詢方法返回的是什么)。

    ·緩存將作為“讀/寫”緩存,意味著檢索的對(duì)象不是共享的且可以被調(diào)用者安全地修改,而不會(huì)被其它調(diào)用者或者線程干擾。

    所有這些特性都可以通過(guò)cache元素進(jìn)行修改。例如:

    <cache

    eviction="FIFO"

    flushInterval="60000"

    size="512"

    readOnly="true"/>

    ?

    這種高級(jí)的配置創(chuàng)建一個(gè)每60秒刷新一次的FIFO?緩存,存儲(chǔ)512個(gè)結(jié)果對(duì)象或列表的引用,并且返回的對(duì)象是只讀的。因此在不用的線程里的調(diào)用者修改它們可能會(huì)引用沖突。

    ?

    可用的回收算法如下:

    ·LRU最近最少使用:移出最近最長(zhǎng)時(shí)間內(nèi)都沒(méi)有被使用的對(duì)象。

    ·FIFO先進(jìn)先出:移除最先進(jìn)入緩存的對(duì)象。

    ·SOFT軟引用:?基于垃圾回收機(jī)制和軟引用規(guī)則來(lái)移除對(duì)象(空間內(nèi)存不足時(shí)才進(jìn)行回收)。

    ·WEAK弱引用:基于垃圾回收機(jī)制和弱引用規(guī)則(垃圾回收器掃描到時(shí)即進(jìn)行回收)。

    默認(rèn)使用LRU

    flushInterval:設(shè)置任何正整數(shù),代表一個(gè)以毫秒為單位的合理時(shí)間。默認(rèn)是沒(méi)有設(shè)置,因此沒(méi)有刷新間隔時(shí)間被使用,在語(yǔ)句每次調(diào)用時(shí)才進(jìn)行刷新。

    Size屬性可以設(shè)置為一個(gè)正整數(shù),您需要留意您要緩存對(duì)象的大小和環(huán)境中可用的內(nèi)存空間。默認(rèn)是1024

    readOnly屬性可以被設(shè)置為true?或false。只讀緩存將對(duì)所有調(diào)用者返回同一個(gè)實(shí)例。因此這些對(duì)象都不能被修改,這可以極大的提高性能。可寫的緩存將通過(guò)序列化來(lái)返回一個(gè)緩存對(duì)象的拷貝。這會(huì)比較慢,但是比較安全。所以默認(rèn)值是false

    ?

    使用自定義緩存

    除了上面已經(jīng)定義好的緩存方式,您能夠通過(guò)您自己的緩存實(shí)現(xiàn)來(lái)完全重寫緩存行為,或者通過(guò)創(chuàng)建第三方緩存解決方案的適配器。

    <cache type=”com.domain.something.MyCustomCache”/>

    這個(gè)例子演示了如果自定義緩存實(shí)現(xiàn)。由type指定的類必須實(shí)現(xiàn)org.mybatis.cache.Cache接口。這個(gè)接口是MyBatis框架比較復(fù)雜的接口之一,先給個(gè)示例:

    public interface Cache {

    String getId();

    int getSize();

    void putObject(Object key, Object value);

    Object getObject(Object key);

    boolean hasKey(Object key);

    Object removeObject(Object key);

    void clear();

    ReadWriteLock getReadWriteLock();

    }

    ?

    要配置您的緩存,簡(jiǎn)單地添加一個(gè)公共的JavaBeans?屬性到您的緩存實(shí)現(xiàn)中,然后通過(guò)cache?元素設(shè)置屬性進(jìn)行傳遞,下面示例,將在您的緩存實(shí)現(xiàn)上調(diào)用一個(gè)setCacheFile(String file)方法。

    ?

    <cache type=”com.domain.something.MyCustomCache”>

    <property name=”cacheFile” value=”/tmp/my-custom-cache.tmp”/>

    </cache>

    ?

    您可以使用所有簡(jiǎn)單的JavaBeans屬性,MyBatis會(huì)自動(dòng)進(jìn)行轉(zhuǎn)換。

    需要牢記的是一個(gè)緩存配置和緩存實(shí)例都綁定到一個(gè)SQL Map?文件命名空間。因此,所有的這個(gè)相同命名空間的語(yǔ)句也都和這個(gè)緩存綁定。語(yǔ)句可以修改如何與這個(gè)緩存相匹配,或者使用兩個(gè)簡(jiǎn)單的屬性來(lái)完全排除它們自己。默認(rèn)情況下,語(yǔ)句像下面這樣來(lái)配置:

    <select ... flushCache=”false” useCache=”true”/>

    <insert ... flushCache=”true”/>

    <update ... flushCache=”true”/>

    <delete ... flushCache=”true”/>?

    因?yàn)橛心J(rèn)值,所以您不需要使用這種方式明確地配置這些語(yǔ)句。如果您想改變默認(rèn)的動(dòng)作,只需要設(shè)置flushCacheuseCache?屬性即可。舉個(gè)例子來(lái)說(shuō),在許多的場(chǎng)合下您可能排除緩存中某些特定的select語(yǔ)句。或者您想用select語(yǔ)句清空緩存。同樣的,您也可能有一些update?語(yǔ)句在執(zhí)行的時(shí)候不需要清空緩存。

    ?

    cache-ref元素

    回想上一節(jié),我們僅僅只是討論在某一個(gè)命名空間里使用或者刷新緩存。但有可能您想要在不同的命名空間里共享同一個(gè)緩存配置或者實(shí)例。在這種情況下,您就可以使用cache-ref?元素來(lái)引用另外一個(gè)緩存。

    <cache-ref namespace=”com.someone.application.data.SomeMapper”/>


    總結(jié)

    以上是生活随笔為你收集整理的Mybatis 高级结果映射 ResultMap Association Collection的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。