MyBatis的創建基於這樣一個思想:數據庫並不是您想怎樣就怎樣的。雖然我們希望所有的數據庫遵守第三範式或BCNF(修正的第三範式),但它們不是。如果有一個數據庫能夠完美映射到所有應用程序,也將是非常棒的,但也沒有。結果集映射就是MyBatis爲解決這些問題而提供的解決方案。例如,我們如何映射下面這條語句
- <select id="selectBlog_by_id" parameterType="int" resultMap="Blog_result">
- select
- b.id,
- b.title,
- b.author_id,
- a.id,
- a.username,
- a.password,
- a.email,
- a.bio
- from
- Blog b left join Author a
- on
- b.author_id = a.id
- where
- b.id = #{id}
- </select>
- <resultMap type="Blog" id="Blog_result">
- <id column="id" property="id" />
- <result column="title" property="title"/>
- <!-- 映射關聯的對象 -->
- <association property="author" javaType="Author">
- <id column="author_id" property="id"/>
- <result column="username" property="username"/>
- <result column="password" property="password"/>
- <result column="email" property="email"/>
- <result column="bio" property="bio"/>
- </association>
- </resultMap>
resultMap
resultMap屬性:type爲java實體類;id爲此resultMap的標識。
resultMap可以設置的映射:
1. constructor – 用來將結果反射給一個實例化好的類的構造器
a) idArg – ID 參數;將結果集標記爲ID,以方便全局調用
b) arg –反射到構造器的通常結果
2. id – ID 結果,將結果集標記爲ID,以方便全局調用
3. result – 反射到JavaBean 屬性的普通結果
4. association – 複雜類型的結合;多個結果合成的類型
a) nested result mappings – 幾resultMap 自身嵌套關聯,也可以引用到一個其它上
5. collection –複雜類型集合a collection of complex types
6. nested result mappings – resultMap 的集合,也可以引用到一個其它上
7. discriminator – 使用一個結果值以決定使用哪個resultMap
a) case – 基本一些值的結果映射的case 情形
i. nested result mappings –一個case 情形本身就是一個結果映射,因此也可以包括一些相同的元素,也可以引用一個外部resultMap。
下面一部分將詳細說明每個元素。
id、result
- <resultMap type="Blog" id="Blog_result">
- <id column="id" property="id" />
- <result column="title" property="title"/>
- </resultMap>
這些是結果映射最基本內容。id和result都映射一個單獨列的值到簡單數據類型(字符串,整型,雙精度浮點數,日期等)的單獨屬性或字段。
這兩者之間的唯一不同是id表示的結果將是當比較對象實例時用到的標識屬性。這幫助來改進整體表現,特別是緩存和嵌入結果映射(也就是聯合映射)。
id、result語句屬性配置細節:
屬性 |
描述 |
property |
映射到列結果的字段或屬性。如果匹配的是存在的,和給定名稱相同的JavaBeans的屬性,那麼就會使用。 否則MyBatis將會尋找給定名稱的字段。這兩種情形你可以使用通常點式的複雜屬性導航。 比如,你可以這樣映射一些東西:“username”,或者映射到一些複雜的東西:“address.street.number”。 |
column |
從數據庫中得到的列名,或者是列名的重命名標籤。 這也是通常和會傳遞給resultSet.getString(columnName)方法參數中相同的字符串。 |
javaType |
一個Java類的完全限定名,或一個類型別名(參加上面內建類型別名的列表)。 如果你映射到一個JavaBean,MyBatis通常可以斷定類型。 然而,如果你映射到的是HashMap,那麼你應該明確地指定javaType來保證所需的行爲。 |
jdbcType |
在這個表格之後的所支持的JDBC類型列表中的類型。JDBC類型是僅僅需要對插入,更新和刪除操作可能爲空的列進行處理。 這是JDBC的需要,而不是MyBatis的。如果你直接使用JDBC編程,你需要指定這個類型-但僅僅對可能爲空的值。 |
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 <構造方法>
- <resultMap type="Blog" id="Blog_result_cs">
- <constructor>
- <idArg column="id" javaType="int"/>
- <arg column="title" javaType="String"/>
- </constructor>
- </resultMap>
對於大多數數據傳輸對象(Data Transfer Object,DTO)類型,屬性可以起作用,而且像絕大多數的領域模型,指令也許是你想使用一成不變的類的地方。通常包含引用或查詢數據的表很少或基本不變的話對一成不變的類來說是合適的。構造方法注入允許你在初始化時爲類設置屬性的值,而不用暴露出公有方法。MyBatis也支持私有屬性和私有JavaBeans屬性來達到這個目的,但是一些人更青睞構造方法注入。Constructor(構造方法)元素支持這個。
- package com.accp.mybatis.model;
- public class Blog {
- private Integer id;
- private String title;
- private Integer authorId;
- public Blog() {
- }
- public Blog(Integer id, String title, Integer authorId) {
- super();
- System.out.println("使用有參Blog");
- this.id = id;
- this.title = title;
- this.authorId = authorId;
- }
- //........
- }
爲了向這個構造方法中注入結果,MyBatis需要通過它的參數的類型來標識構造方法。Java沒有自查(或反射)參數名的方法。所以當創建一個構造方法元素時,保證參數是按順序排列的,而且數據類型也是確定的。
association<關聯映射>
- <!-- 映射關聯的對象 -->
- <association property="author" javaType="Author">
- <id column="author_id" property="id"/>
- <result column="username" property="username"/>
- <result column="password" property="password"/>
- <result column="email" property="email"/>
- <result column="bio" property="bio"/>
- </association>
關聯元素處理“有一個”類型的關係。比如,在我們的示例中,一個博客有一個用戶。關聯映射就工作於這種結果之上。你指定了目標屬性,來獲取值的列,屬性的java類型(很多情況下MyBatis可以自己算出來),如果需要的話還有jdbc類型,如果你想覆蓋或獲取的結果值還需要類型控制器。
關聯中不同的是你需要告訴MyBatis如何加載關聯。MyBatis在這方面會有兩種不同的方式:
(1) 嵌套查詢:通過執行另外一個SQL映射語句來返回預期的複雜類型。
(2) 嵌套結果:使用嵌套結果映射來處理重複的聯合結果的子集。
首先,然讓我們來查看這個元素的屬性。
屬性 |
描述 |
property |
映射到列結果的字段或屬性。如果匹配的是存在的,和給定名稱相同的JavaBeans的屬性,那麼就會使用。否則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類型是僅僅需要對插入,更新和刪除操作可能爲空的列進行處理。這是JDBC的需要,而不是MyBatis的。如果你直接使用JDBC編程,你需要指定這個類型-但僅僅對可能爲空的值。 |
typeHandler |
我們在前面討論過默認的類型處理器。使用這個屬性,你可以覆蓋默認的類型處理器。這個屬性值是類的完全限定名或者是一個類型處理器的實現,或者是類型別名。 |
聯合嵌套選擇(Nested Select for Association)
select |
通過這個屬性,通過ID引用另一個加載複雜類型的映射語句。從指定列屬性中返回的值,將作爲參數設置給目標select 語句。表格下方將有一個例子。注意:在處理組合鍵時,您可以使用column=”{prop1=col1,prop2=col2}”這樣的語法,設置多個列名傳入到嵌套語句。這就會把prop1和prop2設置到目標嵌套語句的參數對象中。 |
SELECT<聯合查詢>
- <resultMap type="Blog" id="Blog_result">
- <association property="author" column="author_id"
- javaType="Author" select="selectAuthorById" />
- </resultMap>
- <select id="selectAuthorById" parameterType="int" resultType="Author">
- select * from Author where id = #{id}
- </select>
- <!--
- select關聯,建議在一對一的情況下使用。
- 在此處,如果selectBlogById返回多個Blog,將會帶來N+1問題
- -->
- <select id="selectBlogById" parameterType="int" resultMap="Blog_result">
- select * from Blog where id = #{id}
- </select>
我們有兩個查詢語句:一個來加載博客,另外一個來加載作者,而且博客的結果映射描述了“selectAuthorById”語句應該被用來加載它的author屬性。
其他所有的屬性將會被自動加載,假設它們的列和屬性名相匹配。
這種方式很簡單,但是對於大型數據集合和列表將不會表現很好。問題就是我們熟知的“N+1查詢問題”。概括地講,N+1查詢問題可以是這樣引起的:
(1)你執行了一個單獨的SQL語句來獲取結果列表(就是“+1”)。
(2)對返回的每條記錄,你執行了一個查詢語句來爲每個加載細節(就是“N”)。
關聯的嵌套結果
resultMap |
這是結果映射的ID,可以映射關聯的嵌套結果到一個合適的對象圖中。這是一種替代方法來調用另外一個查詢語句。這允許你聯合多個表來合成到一個單獨的結果集。這樣的結果集可能包含重複,數據的重複組需要被分解,合理映射到一個嵌套的對象圖。爲了使它變得容易,MyBatis讓你“鏈接”結果映射,來處理嵌套結果。例子會很容易來仿照,這個表格後面也有一個示例。 |
- <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>
注意這個聯合查詢,以及採取保護來確保所有結果被唯一而且清晰的名字來重命名。這使得映射非常簡單。現在我們可以映射這個結果:
- <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>
非常重要:在嵌套結果映射中id元素扮演了非常重要的角色。應該通常指定一個或多個屬性,它們可以用來唯一標識結果。實際上就是如果你不使用它(id元素),但是會產生一個嚴重的性能問題,不過MyBatis仍然可以正常工作。選擇的屬性越少越好,它們可以唯一地標識結果。主鍵就是一個顯而易見的選擇(即便是聯合主鍵)。
現在,上面的示例用了外部的結果映射元素來映射關聯。這使得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>
collection<集合>
- <collection property="posts" ofType="Post">
- <id property="id" column="id"/>
- <result property="subject" column="subject"/>
- <result property="body" column="body"/>
- </collection>
集合元素的作用幾乎和關聯是相同的。實際上,它們也很相似。
我們來繼續上面的示例,一個博客只有一個作者。但是博客有很多文章。在博客類中,這可以由下面這樣的寫法來表示:
private List<Post> posts;
要映射嵌套結果集合到List中,我們使用集合元素。就像關聯元素一樣,我們可以從連接中使用嵌套查詢,或者嵌套結果。
集合的嵌套查詢
首先,讓我們看看使用嵌套查詢來爲博客加載文章。
- <resultMap type="Blog" id="Blog_result">
- <association property="author" column="author_id"
- javaType="Author" select="selectAuthorById" />
- <collection property="posts" javaType="ArrayList" column="id" ofType="Post" select="selectBlogPost" />
- </resultMap>
- <select id="selectBlogPost" resultType="Post" parameterType="int">
- select * from Post where blog_id=#{id}
- </select>
- <select id="selectAuthorById" parameterType="int" resultType="Author">
- select * from Author where id = #{id}
- </select>
- <!--
- select關聯,建議在一對一的情況下使用。
- 在此處,如果selectBlogById返回多個Blog,將會帶來N+1問題
- -->
- <select id="selectBlogById" parameterType="int" resultMap="Blog_result">
- select * from Blog where id = #{id}
- </select>
首先,你應該注意我們使用的是集合元素。然後要注意那個新的“ofType”屬性。這個屬性用來區分JavaBean(或字段)屬性類型和集合包含的類型來說是很重要的。所以你可以讀出下面這個映射:
- <collection property="posts" javaType="ArrayList" column="id" ofType="Post" select="selectBlogPost" />
讀作:“在Post類型的ArrayList中的posts的集合。”
集合的嵌套結果
- <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>
現在用文章映射集合映射博客,可以簡單寫爲:
- <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元素的重要性。
如果你引用更長的形式允許你的結果映射的更多重用,你可以使用下面這個替代的映
- <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>
discriminator鑑別器
- <discriminator javaType="int" column="draft">
- <case value="1" resultType="DraftPost"/>
- </discriminator>
有時一個單獨的數據庫查詢也許返回很多不同(但是希望有些關聯)數據類型的結果集。鑑別器元素就是被設計來處理這個情況的,還有包括類的繼承層次結構。鑑別器非常容易理解,因爲它的表現很像Java語言中的switch語句。
定義鑑別器指定了column和javaType屬性。列是MyBatis查找比較值的地方。JavaType是需要被用來保證等價測試的合適類型(儘管字符串在很多情形下都會有用)。比如:
- <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>
- <resultMap id="carResult" type="Car">
- <result property=”doorCount” column="door_count" />
- </resultMap>
上面示例中,MyBatis會從結果集中得到每條記錄,然後比較它的vehicle類型的值。如果它匹配任何一個鑑別器的實例,那麼就使用這個實例指定的結果映射。
還有另外一種語法來做簡潔的映射風格。比如:
- <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>
- <select id="selectBlog_by_id" parameterType="int" resultMap="Blog_result">
- select
- b.id,
- b.title,
- b.author_id,
- a.id,
- a.username,
- a.password,
- a.email,
- a.bio
- from
- Blog b left join Author a
- on
- b.author_id = a.id
- where
- b.id = #{id}
- </select>
- <resultMap type="Blog" id="Blog_result">
- <id column="id" property="id" />
- <result column="title" property="title"/>
- <!-- 映射關聯的對象 -->
- <association property="author" javaType="Author">
- <id column="author_id" property="id"/>
- <result column="username" property="username"/>
- <result column="password" property="password"/>
- <result column="email" property="email"/>
- <result column="bio" property="bio"/>
- </association>
- </resultMap>
resultMap
resultMap屬性:type爲java實體類;id爲此resultMap的標識。
resultMap可以設置的映射:
1. constructor – 用來將結果反射給一個實例化好的類的構造器
a) idArg – ID 參數;將結果集標記爲ID,以方便全局調用
b) arg –反射到構造器的通常結果
2. id – ID 結果,將結果集標記爲ID,以方便全局調用
3. result – 反射到JavaBean 屬性的普通結果
4. association – 複雜類型的結合;多個結果合成的類型
a) nested result mappings – 幾resultMap 自身嵌套關聯,也可以引用到一個其它上
5. collection –複雜類型集合a collection of complex types
6. nested result mappings – resultMap 的集合,也可以引用到一個其它上
7. discriminator – 使用一個結果值以決定使用哪個resultMap
a) case – 基本一些值的結果映射的case 情形
i. nested result mappings –一個case 情形本身就是一個結果映射,因此也可以包括一些相同的元素,也可以引用一個外部resultMap。
下面一部分將詳細說明每個元素。
id、result
- <resultMap type="Blog" id="Blog_result">
- <id column="id" property="id" />
- <result column="title" property="title"/>
- </resultMap>
這些是結果映射最基本內容。id和result都映射一個單獨列的值到簡單數據類型(字符串,整型,雙精度浮點數,日期等)的單獨屬性或字段。
這兩者之間的唯一不同是id表示的結果將是當比較對象實例時用到的標識屬性。這幫助來改進整體表現,特別是緩存和嵌入結果映射(也就是聯合映射)。
id、result語句屬性配置細節:
屬性 |
描述 |
property |
映射到列結果的字段或屬性。如果匹配的是存在的,和給定名稱相同的JavaBeans的屬性,那麼就會使用。 否則MyBatis將會尋找給定名稱的字段。這兩種情形你可以使用通常點式的複雜屬性導航。 比如,你可以這樣映射一些東西:“username”,或者映射到一些複雜的東西:“address.street.number”。 |
column |
從數據庫中得到的列名,或者是列名的重命名標籤。 這也是通常和會傳遞給resultSet.getString(columnName)方法參數中相同的字符串。 |
javaType |
一個Java類的完全限定名,或一個類型別名(參加上面內建類型別名的列表)。 如果你映射到一個JavaBean,MyBatis通常可以斷定類型。 然而,如果你映射到的是HashMap,那麼你應該明確地指定javaType來保證所需的行爲。 |
jdbcType |
在這個表格之後的所支持的JDBC類型列表中的類型。JDBC類型是僅僅需要對插入,更新和刪除操作可能爲空的列進行處理。 這是JDBC的需要,而不是MyBatis的。如果你直接使用JDBC編程,你需要指定這個類型-但僅僅對可能爲空的值。 |
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 <構造方法>
- <resultMap type="Blog" id="Blog_result_cs">
- <constructor>
- <idArg column="id" javaType="int"/>
- <arg column="title" javaType="String"/>
- </constructor>
- </resultMap>
對於大多數數據傳輸對象(Data Transfer Object,DTO)類型,屬性可以起作用,而且像絕大多數的領域模型,指令也許是你想使用一成不變的類的地方。通常包含引用或查詢數據的表很少或基本不變的話對一成不變的類來說是合適的。構造方法注入允許你在初始化時爲類設置屬性的值,而不用暴露出公有方法。MyBatis也支持私有屬性和私有JavaBeans屬性來達到這個目的,但是一些人更青睞構造方法注入。Constructor(構造方法)元素支持這個。
- package com.accp.mybatis.model;
- public class Blog {
- private Integer id;
- private String title;
- private Integer authorId;
- public Blog() {
- }
- public Blog(Integer id, String title, Integer authorId) {
- super();
- System.out.println("使用有參Blog");
- this.id = id;
- this.title = title;
- this.authorId = authorId;
- }
- //........
- }
爲了向這個構造方法中注入結果,MyBatis需要通過它的參數的類型來標識構造方法。Java沒有自查(或反射)參數名的方法。所以當創建一個構造方法元素時,保證參數是按順序排列的,而且數據類型也是確定的。
association<關聯映射>
- <!-- 映射關聯的對象 -->
- <association property="author" javaType="Author">
- <id column="author_id" property="id"/>
- <result column="username" property="username"/>
- <result column="password" property="password"/>
- <result column="email" property="email"/>
- <result column="bio" property="bio"/>
- </association>
關聯元素處理“有一個”類型的關係。比如,在我們的示例中,一個博客有一個用戶。關聯映射就工作於這種結果之上。你指定了目標屬性,來獲取值的列,屬性的java類型(很多情況下MyBatis可以自己算出來),如果需要的話還有jdbc類型,如果你想覆蓋或獲取的結果值還需要類型控制器。
關聯中不同的是你需要告訴MyBatis如何加載關聯。MyBatis在這方面會有兩種不同的方式:
(1) 嵌套查詢:通過執行另外一個SQL映射語句來返回預期的複雜類型。
(2) 嵌套結果:使用嵌套結果映射來處理重複的聯合結果的子集。
首先,然讓我們來查看這個元素的屬性。
屬性 |
描述 |
property |
映射到列結果的字段或屬性。如果匹配的是存在的,和給定名稱相同的JavaBeans的屬性,那麼就會使用。否則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類型是僅僅需要對插入,更新和刪除操作可能爲空的列進行處理。這是JDBC的需要,而不是MyBatis的。如果你直接使用JDBC編程,你需要指定這個類型-但僅僅對可能爲空的值。 |
typeHandler |
我們在前面討論過默認的類型處理器。使用這個屬性,你可以覆蓋默認的類型處理器。這個屬性值是類的完全限定名或者是一個類型處理器的實現,或者是類型別名。 |
聯合嵌套選擇(Nested Select for Association)
select |
通過這個屬性,通過ID引用另一個加載複雜類型的映射語句。從指定列屬性中返回的值,將作爲參數設置給目標select 語句。表格下方將有一個例子。注意:在處理組合鍵時,您可以使用column=”{prop1=col1,prop2=col2}”這樣的語法,設置多個列名傳入到嵌套語句。這就會把prop1和prop2設置到目標嵌套語句的參數對象中。 |
SELECT<聯合查詢>
- <resultMap type="Blog" id="Blog_result">
- <association property="author" column="author_id"
- javaType="Author" select="selectAuthorById" />
- </resultMap>
- <select id="selectAuthorById" parameterType="int" resultType="Author">
- select * from Author where id = #{id}
- </select>
- <!--
- select關聯,建議在一對一的情況下使用。
- 在此處,如果selectBlogById返回多個Blog,將會帶來N+1問題
- -->
- <select id="selectBlogById" parameterType="int" resultMap="Blog_result">
- select * from Blog where id = #{id}
- </select>
我們有兩個查詢語句:一個來加載博客,另外一個來加載作者,而且博客的結果映射描述了“selectAuthorById”語句應該被用來加載它的author屬性。
其他所有的屬性將會被自動加載,假設它們的列和屬性名相匹配。
這種方式很簡單,但是對於大型數據集合和列表將不會表現很好。問題就是我們熟知的“N+1查詢問題”。概括地講,N+1查詢問題可以是這樣引起的:
(1)你執行了一個單獨的SQL語句來獲取結果列表(就是“+1”)。
(2)對返回的每條記錄,你執行了一個查詢語句來爲每個加載細節(就是“N”)。
關聯的嵌套結果
resultMap |
這是結果映射的ID,可以映射關聯的嵌套結果到一個合適的對象圖中。這是一種替代方法來調用另外一個查詢語句。這允許你聯合多個表來合成到一個單獨的結果集。這樣的結果集可能包含重複,數據的重複組需要被分解,合理映射到一個嵌套的對象圖。爲了使它變得容易,MyBatis讓你“鏈接”結果映射,來處理嵌套結果。例子會很容易來仿照,這個表格後面也有一個示例。 |
- <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>
注意這個聯合查詢,以及採取保護來確保所有結果被唯一而且清晰的名字來重命名。這使得映射非常簡單。現在我們可以映射這個結果:
- <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>
非常重要:在嵌套結果映射中id元素扮演了非常重要的角色。應該通常指定一個或多個屬性,它們可以用來唯一標識結果。實際上就是如果你不使用它(id元素),但是會產生一個嚴重的性能問題,不過MyBatis仍然可以正常工作。選擇的屬性越少越好,它們可以唯一地標識結果。主鍵就是一個顯而易見的選擇(即便是聯合主鍵)。
現在,上面的示例用了外部的結果映射元素來映射關聯。這使得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>
collection<集合>
- <collection property="posts" ofType="Post">
- <id property="id" column="id"/>
- <result property="subject" column="subject"/>
- <result property="body" column="body"/>
- </collection>
集合元素的作用幾乎和關聯是相同的。實際上,它們也很相似。
我們來繼續上面的示例,一個博客只有一個作者。但是博客有很多文章。在博客類中,這可以由下面這樣的寫法來表示:
private List<Post> posts;
要映射嵌套結果集合到List中,我們使用集合元素。就像關聯元素一樣,我們可以從連接中使用嵌套查詢,或者嵌套結果。
集合的嵌套查詢
首先,讓我們看看使用嵌套查詢來爲博客加載文章。
- <resultMap type="Blog" id="Blog_result">
- <association property="author" column="author_id"
- javaType="Author" select="selectAuthorById" />
- <collection property="posts" javaType="ArrayList" column="id" ofType="Post" select="selectBlogPost" />
- </resultMap>
- <select id="selectBlogPost" resultType="Post" parameterType="int">
- select * from Post where blog_id=#{id}
- </select>
- <select id="selectAuthorById" parameterType="int" resultType="Author">
- select * from Author where id = #{id}
- </select>
- <!--
- select關聯,建議在一對一的情況下使用。
- 在此處,如果selectBlogById返回多個Blog,將會帶來N+1問題
- -->
- <select id="selectBlogById" parameterType="int" resultMap="Blog_result">
- select * from Blog where id = #{id}
- </select>
首先,你應該注意我們使用的是集合元素。然後要注意那個新的“ofType”屬性。這個屬性用來區分JavaBean(或字段)屬性類型和集合包含的類型來說是很重要的。所以你可以讀出下面這個映射:
- <collection property="posts" javaType="ArrayList" column="id" ofType="Post" select="selectBlogPost" />
讀作:“在Post類型的ArrayList中的posts的集合。”
集合的嵌套結果
- <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>
現在用文章映射集合映射博客,可以簡單寫爲:
- <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元素的重要性。
如果你引用更長的形式允許你的結果映射的更多重用,你可以使用下面這個替代的映
- <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>
discriminator鑑別器
- <discriminator javaType="int" column="draft">
- <case value="1" resultType="DraftPost"/>
- </discriminator>
有時一個單獨的數據庫查詢也許返回很多不同(但是希望有些關聯)數據類型的結果集。鑑別器元素就是被設計來處理這個情況的,還有包括類的繼承層次結構。鑑別器非常容易理解,因爲它的表現很像Java語言中的switch語句。
定義鑑別器指定了column和javaType屬性。列是MyBatis查找比較值的地方。JavaType是需要被用來保證等價測試的合適類型(儘管字符串在很多情形下都會有用)。比如:
- <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>
- <resultMap id="carResult" type="Car">
- <result property=”doorCount” column="door_count" />
- </resultMap>
上面示例中,MyBatis會從結果集中得到每條記錄,然後比較它的vehicle類型的值。如果它匹配任何一個鑑別器的實例,那麼就使用這個實例指定的結果映射。
還有另外一種語法來做簡潔的映射風格。比如:
- <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>