mybatis 高級映射

高級結果映射

MyBatis的建立基於這樣一個思想:數據庫並非您想怎樣就怎樣的。雖然咱們但願全部的數據庫遵照第三範式或BCNF(修正的第三範式),但它們不是。若是有一個數據庫可以完美映射到全部應用程序,也將是很是棒的,但也沒有。結果集映射就是MyBatis爲解決這些問題而提供的解決方案。例如,咱們如何映射下面這條語句?html

<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>


您可能想要把它映射到一個智能的對象模型,包括由一個做者寫的一個博客,有許多文章(Post,帖子),每一個文章由0個或者多個評論和標籤。下面是一個複雜ResultMap 的完整例子(假定做者、博客、文章、評論和標籤都是別名)。仔細看看這個例子,可是不用太擔憂,咱們會一步步地來分析,一眼看上去可能讓人沮喪,可是實際上很是簡單的java

<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>

 

這個resultMap 的元素的子元素比較多,討論起來比較寬泛。下面咱們從概念上概覽一下這個resultMap的元素。算法

resultMapsql

·constructor–實例化的時候經過構造器將結果集註入到類中數據庫

oidArg– ID 參數; 將結果集標記爲ID,以方便全局調用緩存

oarg–注入構造器的結果集安全

·id–結果集ID,將結果集標記爲ID,以方便全局調用mybatis

·result–注入一個字段或者javabean屬性的結果app

·association–複雜類型聯合;許多查詢結果合成這個類型框架

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

·collection–複雜類型集合

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

·discriminator–使用一個結果值以決定使用哪一個resultMap

ocase–基於不一樣值的結果映射

§嵌套結果映射–case也能引用它自身, 因此也能包含這些一樣的元素。它也能夠從外部引用resultMap

 

è最佳實踐:逐步地生成resultMap,單元測試對此很是有幫助。若是您嘗試一會兒就生成像上面這樣巨大的resultMap,可能會出錯,而且工做起來很是吃力。從簡單地開始,再一步步地擴展,而且進行單元測試。使用框架開發有一個缺點,它們有時像是一個黑合。爲了確保達到您所預想的行爲,最好的方式就是進行單元測試。這對提交bugs 也很是有用。

 

下一節,咱們一步步地查看這些細節。

id, result元素

 

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

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

 

這是最基本的結果集映射。id 和result 將列映射到屬性或簡單的數據類型字段(String, int, double, Date等)。

這二者惟一不一樣的是,在比較對象實例時id 做爲結果集的標識屬性。這有助於提升整體性能,特別是應用緩存和嵌套結果映射的時候。

 

Id、result屬性以下:

 

Attribute

Description

property

映射數據庫列的字段或屬性。若是JavaBean 的屬性與給定的名稱匹配,就會使用匹配的名字。不然,MyBatis 將搜索給定名稱的字段。兩種狀況下您均可以使用逗點的屬性形式。好比,您能夠映射到「username」,也能夠映射到「address.street.number」。

column

數據庫的列名或者列標籤別名。與傳遞給resultSet.getString(columnName)的參數名稱相同。

javaType

完整java類名或別名(參考上面的內置別名列表)。若是映射到一個JavaBean,那MyBatis 一般會自行檢測到。然而,若是映射到一個HashMap,那您應該明確指定javaType 來確保所需行爲。

jdbcType

這張表下面支持的JDBC類型列表列出的JDBC類型。這個屬性只在insert,update或delete 的時候針對容許空的列有用。JDBC 須要這項,但MyBatis 不須要。若是您直接編寫JDBC代碼,在容許爲空值的狀況下須要指定這個類型。

typeHandler

咱們已經在文檔中討論過默認類型處理器。使用這個屬性能夠重寫默認類型處理器。它的值能夠是一個TypeHandler實現的完整類名,也能夠是一個類型別名。

 

支持的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>

 

當屬性與DTO,或者與您本身的域模型一塊兒工做的時候,許多場合要用到不變類。一般,包含引用,或者查找的數據不多或者數據不會改變的的表,適合映射到不變類中。構造器注入容許您在類實例化後給類設值,這不須要經過public方法。MyBatis一樣也支持private屬性和JavaBeans的私有屬性達到這一點,可是一些用戶可能更喜歡使用構造器注入。構造器元素能夠作到這點。

 

考慮下面的構造器:

 

public class User {

//…

public User(int id, String username) {

//…

}

//…

}

 

爲了將結果注入構造器,MyBatis須要使用它的參數類型來標記構造器。Java沒有辦法經過參數名稱來反射得到。所以當建立constructor 元素,確保參數是按順序的而且指定了正確的類型。

 

<constructor>

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

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

</constructor>

 

其它的屬性與規則與id、result元素的同樣。

 

Attribute

Description

column

數據庫的列名或者列標籤別名。與傳遞給resultSet.getString(columnName)的參數名稱相同。

javaType

完整java類名或別名(參考上面的內置別名列表)。若是映射到一個JavaBean,那MyBatis 一般會自行檢測到。然而,若是映射到一個HashMap,那您應該明確指定javaType 來確保所需行爲。

jdbcType

支持的JDBC類型列表中列出的JDBC類型。這個屬性只在insert,update 或delete 的時候針對容許空的列有用。JDBC 須要這項,但MyBatis 不須要。若是您直接編寫JDBC代碼,在容許爲空值的狀況下須要指定這個類型。

typeHandler

咱們已經在文檔中討論過默認類型處理器。使用這個屬性能夠重寫默認類型處理器。它的值能夠是一個TypeHandler實現的完整類名,也能夠是一個類型別名。

 

 

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」(一對一)這種類型關係。好比在咱們的例子中,一個Blog有一個Author。聯合映射與其它的結果集映射工做方式差很少,指定property、column、javaType(一般MyBatis會自動識別)、jdbcType(若是須要)、typeHandler。

不一樣的地方是您須要告訴MyBatis 如何加載一個聯合查詢。MyBatis使用兩種方式來加載:

·Nested Select:經過執行另外一個返回預期複雜類型的映射SQL語句(即引用外部定義好的SQL語句塊)。

·Nested Results:經過嵌套結果映射(nested result mappings)來處理聯接結果集(joined results)的重複子集。

首先,讓咱們檢查一下元素屬性。正如您看到的,它不一樣於普通只有select和resultMap屬性的結果映射。

Attribute

Description

property

映射數據庫列的字段或屬性。若是JavaBean 的屬性與給定的名稱匹配,就會使用匹配的名字。不然,MyBatis 將搜索給定名稱的字段。兩種狀況下您均可以使用逗點的屬性形式。好比,您能夠映射到」username」,也能夠映射到更復雜點的」address.street.number」。

column

數據庫的列名或者列標籤別名。與傳遞給resultSet.getString(columnName)的參數名稱相同。

注意: 在處理組合鍵時,您可使用column= 「{prop1=col1,prop2=col2}」這樣的語法,設置多個列名傳入到嵌套查詢語句。這就會把prop1和prop2設置到目標嵌套選擇語句的參數對象中。

javaType

完整java類名或別名(參考上面的內置別名列表)。若是映射到一個JavaBean,那MyBatis 一般會自行檢測到。然而,若是映射到一個HashMap,那您應該明確指定javaType 來確保所需行爲。

jdbcType

支持的JDBC類型列表中列出的JDBC類型。這個屬性只在insert,update 或delete 的時候針對容許空的列有用。JDBC 須要這項,但MyBatis 不須要。若是您直接編寫JDBC代碼,在容許爲空值的狀況下須要指定這個類型。

typeHandler

咱們已經在文檔中討論過默認類型處理器。使用這個屬性能夠重寫默認類型處理器。它的值能夠是一個TypeHandler實現的完整類名,也能夠是一個類型別名。

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

select

經過這個屬性,經過ID引用另外一個加載複雜類型的映射語句。從指定列屬性中返回的值,將做爲參數設置給目標select 語句。表格下方將有一個例子。注意:在處理組合鍵時,您可使用column=」{prop1=col1,prop2=col2}」這樣的語法,設置多個列名傳入到嵌套語句。這就會把prop1和prop2設置到目標嵌套語句的參數對象中。

 例如:  

<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>

咱們使用兩個select語句:一個用來加載Blog,另外一個用來加載Author。Blog的resultMap 描述了使用「selectAuthor」語句來加載author的屬性。

若是列名和屬性名稱相匹配的話,全部匹配的屬性都會自動加載。

 

譯者注:

上面的例子,首先執行<select id=「selectBlog」>,執行結果存放到<resultMap id=「blogResult」>結果映射中。「blogResult」是一個Blog類型,從<select id=「selectBlog」>查出的數據都會自動賦值給」blogResult」的與列名匹配的屬性,這時blog_id,title等就被賦值了。同時「blogResult」還有一個關聯屬性"Author",執行嵌套查詢select=」selectAuthor」後,Author對象的屬性id,username,password,email,bio也被賦於數據庫匹配的值。

 

Blog

{

blog_id;

title;

Author author

{

id;

username;

password;

email;

bio;

 

}

 

}

 

 

雖然這個方法簡單,可是對於大數據集或列表查詢,就不盡如人意了。這個問題被稱爲「N+1 選擇問題」(N+1 Selects Problem)。歸納地說,N+1選擇問題是這樣產生的:

·您執行單條SQL語句去獲取一個列表的記錄( 「+1」)。

·對列表中的每一條記錄,再執行一個聯合select 語句來加載每條記錄更加詳細的信息(「N」)。

這個問題會致使成千上萬的SQL語句的執行,所以並不是老是可取的。

上面的例子,MyBatis可使用延遲加載這些查詢,所以這些查詢立馬可節省開銷。然而,若是您加載一個列表後當即迭代訪問嵌套的數據,這將會調用全部的延遲加載,所以性能會變得很是糟糕。

鑑於此,這有另一種方式。

聯合嵌套結果集(Nested Results for Association) 

resultMap

一個能夠映射聯合嵌套結果集到一個適合的對象視圖上的ResultMap 。這是一個替代的方式去調用另外一個select 語句。它容許您去聯合多個表到一個結果集裏。這樣的結果集可能包括冗餘的、重複的須要分解和正確映射到一個嵌套對象視圖的數據組。簡言之,MyBatis 讓您把結果映射‘連接’到一塊兒,用來處理嵌套結果。舉個例子會更好理解,例子在表格下方。

您已經在上面看到了一個很是複雜的嵌套聯合的例子,接下的演示的例子會更簡單一些。咱們把Blog和Author表聯接起來查詢,而不是執行分開的查詢語句:

<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>


注意到這個鏈接(join),要確保全部的別名都是惟一且無歧義的。這使映射容易多了,如今咱們來映射結果集:

 

<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>

 

在上面的例子中,您會看到Blog的做者(「author」)聯合一個「authorResult」結果映射來加載Author實例。

重點提示:id元素在嵌套結果映射中扮演了很是重要的角色,您應該老是指定一個或多個屬性來惟一標識這個結果集。事實上,若是您沒有那樣作,MyBatis也會工做,可是會致使嚴重性能開銷。選擇儘可能少的屬性來惟一標識結果,而使用主鍵是最明顯的選擇(即便是複合主鍵)。

上面的例子使用一個擴展的resultMap 元素來聯合映射。這可以使Author結果映射可重複使用。而後,若是您不須要重用它,您能夠直接嵌套這個聯合結果映射。下面例子就是使用這樣的方式: 

<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>


在上面的例子中您已經看到若是處理「一對一」(「has one」)類型的聯合查詢。可是對於「一對多」(「has many」)的狀況若是處理呢?這個問題在下一節討論。

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>

 

collection元素的做用差很少和association元素的做用同樣。事實上,它們很是類似,以致於再對類似點進行描述會顯得冗餘,所以咱們只關注它們的不一樣點。

繼續咱們上面的例子,一個Blog只有一個Author。但一個Blog有許多帖子(文章)。在Blog類中,會像下面這樣定義相應屬性: 

private List<Post> posts;

映射一個嵌套結果集到一個列表,咱們使用collection元素。就像association 元素那樣,咱們使用嵌套查詢,或者從鏈接中嵌套結果集。 

集合嵌套選擇(Nested Select for Collection)

首先咱們使用嵌套選擇來加載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>

一看上去這有許多東西須要注意,但大部分看起與咱們在association元素中學過的類似。首先,您會注意到咱們使用了collection元素,而後會注意到一個新的屬性「ofType」。這個元素是用來區別JavaBean屬性(或者字段)類型和集合所包括的類型。所以您會讀到下面這段代碼。

 

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

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

è理解爲:「一個名爲posts,類型爲Post的ArrayList集合(A collection of posts in an ArrayList of type Post)」 。

javaType屬性不是必須的,一般MyBatis 會自動識別,因此您一般能夠簡略地寫成:

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

select=」selectPostsForBlog」/>


 

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

這時候,您可能已經猜出嵌套結果集是怎樣工做的了,由於它與association很是類似,只不過多了一個屬性「ofType」

讓咱們看下這個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>


一樣,咱們把Blog和Post兩張錶鏈接在一塊兒,而且也保證列標籤名在映射的時候是惟一且無歧義的。如今將Blog和Post的集合映射在一塊兒是多麼簡單:

<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>

 

再次強調一下,id 元素是很是重要的。若是您忘了或者不知道id 元素的做用,請先讀一下上面association一節。

若是但願結果映射有更好的可重用性,您可使用下面的方式:

<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>

在您的映射中沒有深度、寬度、聯合和集合數目的限制。但應該謹記,在進行映射的時候也要考慮性能的因素。應用程序的單元測試和性能測試幫助您發現最好的方式可能要花很長時間。但幸運的是,MyBatis容許您之後能夠修改您的想法,這時只須要修改少許代碼就好了。

關於高級聯合和集合映射是一個比較深刻的課題,文檔只能幫您瞭解到這裏,多作一些實踐,一切將很快變得容易理解。

 

Discriminator元素

 

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

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

</discriminator>

 

有時候一條數據庫查詢可能會返回包括各類不一樣的數據類型的結果集。Discriminator(識別器)元素被設計來處理這種狀況,以及其它像類繼承層次狀況。識別器很是好理解,它就像java裏的switch語句。

 

Discriminator定義要指定columnjavaType屬性。列是MyBatis將要取出進行比較的值,javaType用來肯定適當的測試是否正確運行(雖然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>

在這個例子中,MyBatis將會從結果集中取出每條記錄,而後比較它的vehicle type的值。若是匹配任何discriminator中的case,它將使用由case指定的resultMap。這是排它性的,換句話說,其它的case的resultMap將會被忽略(除非使用咱們下面說到的extended)。若是沒有匹配到任何case,MyBatis只是簡單的使用定義在discriminator塊外面的resultMap。因此,若是carResult像下面這樣定義:

 

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

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

</resultMap>

 

那麼,只有doorCount屬性會被加載。這樣作是爲了與識別器cases羣組徹底獨立開來,哪怕它與上一層的resultMap一點關係都沒有。在剛纔的例子裏咱們固然知道cars和vehicles的關係,a Car is-a Vehicle。所以,咱們也要把其它屬性加載進來。咱們要稍稍改動一下resultMap:

 

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

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

</resultMap>

 

如今,vehicleResult和carResult的全部屬性都會被加載。

可能有人會認爲這樣擴展映射定義有一點單調了,因此還有一種可選的更加簡單明瞭的映射風格語法。例如:

​
<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>  

​


記住:對於這麼多的結果映射,若是您不指定任何的結果集,那麼MyBatis 會自動地將列名與屬性相匹配。因此上面所舉的例子比實際中須要的要詳細。儘管如此,大部分數據庫有點複雜,而且它並非全部狀況都是徹底能夠適用的。

Cache元素

 

MyBatis包含一個強大的、可配置、可定製的查詢緩存機制。MyBatis 3 的緩存實現有了許多改進,使它更強大更容易配置。默認的狀況,緩存是沒有開啓,除了會話緩存之外,它能夠提升性能,且能解決循環依賴。開啓二級緩存,您只須要在SQL映射文件中加入簡單的一行:

 

<cache/>

 

這句簡單的語句做用以下:

·全部映射文件裏的select語句的結果都會被緩存。

·全部映射文件裏的insert、updatedelete語句執行都會清空緩存。

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

·緩存不會被設定的時間所清空。

·每一個緩存能夠存儲1024 個列表或對象的引用(無論查詢方法返回的是什麼)。

·緩存將做爲「讀/寫」緩存,意味着檢索的對象不是共享的且能夠被調用者安全地修改,而不會被其它調用者或者線程干擾。

全部這些特性均可以經過cache元素進行修改。例如:

<cache

eviction="FIFO"

flushInterval="60000"

size="512"

readOnly="true"/>

 

這種高級的配置建立一個每60秒刷新一次的FIFO 緩存,存儲512個結果對象或列表的引用,而且返回的對象是隻讀的。所以在不用的線程裏的調用者修改它們可能會引用衝突。

 

可用的回收算法以下:

·LRU–最近最少使用:移出最近最長時間內都沒有被使用的對象。

·FIFO–先進先出:移除最早進入緩存的對象。

·SOFT–軟引用: 基於垃圾回收機制和軟引用規則來移除對象(空間內存不足時才進行回收)。

·WEAK–弱引用:基於垃圾回收機制和弱引用規則(垃圾回收器掃描到時即進行回收)。

默認使用LRU。

flushInterval:設置任何正整數,表明一個以毫秒爲單位的合理時間。默認是沒有設置,所以沒有刷新間隔時間被使用,在語句每次調用時才進行刷新。

Size:屬性能夠設置爲一個正整數,您須要留意您要緩存對象的大小和環境中可用的內存空間。默認是1024。

readOnly:屬性能夠被設置爲true 或false。只讀緩存將對全部調用者返回同一個實例。所以這些對象都不能被修改,這能夠極大的提升性能。可寫的緩存將經過序列化來返回一個緩存對象的拷貝。這會比較慢,可是比較安全。因此默認值是false。

 

使用自定義緩存

除了上面已經定義好的緩存方式,您可以經過您本身的緩存實現來徹底重寫緩存行爲,或者經過建立第三方緩存解決方案的適配器。

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

這個例子演示了若是自定義緩存實現。由type指定的類必須實現org.mybatis.cache.Cache接口。這個接口是MyBatis框架比較複雜的接口之一,先給個示例:

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();  
}

 

要配置您的緩存,簡單地添加一個公共的JavaBeans 屬性到您的緩存實現中,而後經過cache 元素設置屬性進行傳遞,下面示例,將在您的緩存實現上調用一個setCacheFile(String file)方法。

 

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

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

</cache>

 

您可使用全部簡單的JavaBeans屬性,MyBatis會自動進行轉換。

須要牢記的是一個緩存配置和緩存實例都綁定到一個SQL Map 文件命名空間。所以,全部的這個相同命名空間的語句也都和這個緩存綁定。語句能夠修改如何與這個緩存相匹配,或者使用兩個簡單的屬性來徹底排除它們本身。默認狀況下,語句像下面這樣來配置:

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

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

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

<delete ... flushCache=」true」/> 

由於有默認值,因此您不須要使用這種方式明確地配置這些語句。若是您想改變默認的動做,只須要設置flushCache和useCache 屬性便可。舉個例子來講,在許多的場合下您可能排除緩存中某些特定的select語句。或者您想用select語句清空緩存。一樣的,您也可能有一些update 語句在執行的時候不須要清空緩存。

 

cache-ref元素

 

回想上一節,咱們僅僅只是討論在某一個命名空間裏使用或者刷新緩存。但有可能您想要在不一樣的命名空間裏共享同一個緩存配置或者實例。在這種狀況下,您就可使用cache-ref 元素來引用另一個緩存。

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

 

例子:

接下來看下一個完整的sqlmap能夠運行的ibatis文件:


java類:

public class Item implements Serializable {  
  
    /** 
     *  
     */  
    private static final long serialVersionUID = 3969923837162162882L;  
  
    /** 
     *  
     */  
    private Long id;  
      
    /** 
     * 商品編碼 
     */  
    private Long numIid;  
      
    /** 
     * 商品標題 
     */  
    private String title;  
      
    /** 
     * 副標題 
     */  
    private String subTitle;  
      
    /** 
     * 商品類目, 
     */  
    private Integer cat;  
      
    /** 
     * 商品主圖地址 
     */  
    private String picUrl;  
      
    /** 
     * 商品縮略圖 
     */  
    private String picThumUrl;  
      
    /** 
     * 商家編碼, 
     */  
    private String outerId;  
      
    /** 
     * 商品屬性 
     */  
    private String props;  
      
    /** 
     * 銷售價格 
     */  
    private Double price;  
      
    /** 
     * 商品的市場價格 
     */  
    private Double marketPrice;  
      
    /** 
     * 商品數量 
     */  
    private Integer num;  
      
    /** 
     * 商品上架時間 
     */  
    private Date listTime;  
      
    /** 
     * 商品下架時間 
     */  
    private Date delistTime;  
      
    /** 
     * 商品銷量 
     */  
    private Integer sales;    
  
    private Date created;  
      
    private Date modified;  
      
    /** 
     * 數據可用狀態,0表示不可用,1表示可用 
     */  
    private Integer enableStatus;  
      
    /** 
     * 商品的url,不存儲數據庫 
     */  
    private String itemTaobaoUrl;  
      
    /** 
     * 商品所關聯的活動 
     */  
    private Active active;  
      
    /** 
     * 該商品的搭配套餐 
     */  
    private List<Item> withPackages;  
  
    //省略了set get方法  
}
相關文章
相關標籤/搜索