resultMap 元素是 MyBatis 中最重要最強大的元素。它就是讓你遠離從結果集中取出數據的JDBC 代碼的那個東西,並且在一些情形下容許你作一些JDBC不支持的事情。ResultMap 的設計就是對於一些簡單的語句咱們不須要明確它們的結果映射,可是到於複雜的語句確實須要描述它們的關係。 java
public class User { private int id; private String username; private String hashedPassword; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getHashedPassword() { return hashedPassword; } public void setHashedPassword(String hashedPassword) { this.hashedPassword = hashedPassword; } } 基於 JavaBean 的規範,上面這個類有 3 個屬性:id,username 和 hashedPassword。這些 在 select 語句中會精確匹配到列名。 這樣的一個 JavaBean 能夠被映射到結果集 : |
select id, username, hashedPassword from some_table where id = #{id} </select> 這些狀況下,MyBatis 會在幕後自動建立一個 ResultMap,基於屬性名來映射列到JavaBean 的屬性上。若是列名沒有精確匹配,你能夠在列名上使用 select 字句的別名(一個基本的SQL特性)來匹配標籤。好比: |
select user_id as id, user_name as username, hashed_Password as hashedPassword from some_table where id = #{id} </select> 還能夠經過下面的這種方式來解決列名和屬性名不一致的狀況: |
<id property="id" column="user_id" /> <result property="username" column="user_name"/> <result property="hashedPassword" column="hashed_password"/> </resultMap> 引用它的語句使用 resultMap 屬性就好了(注意咱們去掉了resultType 屬性,這兩個屬性只能包含其中的一個) <select id=」selectUsers」 parameterType=」int」 resultMap=」userResultMap」> select id, username, hashedPassword from some_table where id = #{id} </select> |
2.高級結果映射 數據庫
對於下面的這個語句子咱們如何進行映射呢? 編程
<!-- Very Complex Statement --> 緩存
<select id="selectBlogDetails" parameterType="int" resultMap="detail edBl ogRes ultMa p"> app select dom B.id as blog_id, post B.title as blog_title, 性能 B.author_id as blog_author_id, this A.id as author_id, spa 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> |
將它映射到一個對象模型,其中包含一個做者寫的博客,有不少的博文,每篇博文有零條或多條的評論和標籤。下面是一個完整的複雜結果映射例子(假設做者,博客, 博文,評論和標籤都是類型的別名)
<!-- 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> |
resultMap 元素有不少子元素和一個值得討論的結構。下面是 resultMap 元素的概念視圖 resultMap
2.1 id,result
id 和 result 都映射一個單獨列的值到簡單數據類型(字符串,整型,雙精度浮點數,日期等)的單獨屬性或字段。這二者之間的惟一不一樣是 id 表示的結果將是當比較對象實例時用到的標識屬性。這幫助來改進總體表現,特別是緩存和嵌入結果映射(也就是聯合映射)。 每一個都有一些屬性:
屬性 |
描述 |
property |
映射到列結果的字段或屬性。若是匹配的是存在的, 和給定名稱相同的JavaBeans 的屬性,那麼就會使用。不然 MyBatis 將會尋找給定名稱的字段。這兩種情形你可使用一般點式的複雜屬性導航。好比,你能夠這樣映射一些東西:「username」,或者映射到一些複雜的東西: 「address.street.number」。 |
column |
從數據庫中獲得的列名 ,或者是列名的重命名標籤。 這也是一般和會傳遞給 resultSet.getString(columnName)方法參數中相同的字符串。 |
javaType |
一個 Java 類的徹底限定名,或一個類型別名(參加上面內建類型別名 的列表)。若是你映射到一個 JavaBean,MyBatis 一般能夠判定類型。 然而,若是你映射到的是 HashMap,那麼你應該明確地指定 javaType 來保證所需的行爲。 |
jdbcType |
在這個表格以後的所支持的 JDBC 類型列表中的類型。JDBC 類型是僅 僅須要對插入,更新和刪除操做可能爲空的列進行處理。這是 JDBC 的須要,而不是 MyBatis 的。若是你直接使用 JDBC 編程,你須要指定 這個類型-但僅僅對可能爲空的值。 |
typeHandler |
使用這個屬性 ,你能夠覆蓋默認的類型處理器。這個屬性值是類的徹底限定名或者 是一個類型處理器的實現,或者是類型別名。 |
2.2構造方法
<constructor >
<idArg column="id" javaType="int"/>
<arg column=」username」 javaType=」String」/>
</constructor>
看看下面這個構造方法:
public class User { //...
public User(int id, String username) { //...
}
//... }
爲了向這個構造方法中注入結果,MyBatis 須要經過它的參數的類型來標識構造方法。 Java 沒有自查(反射)參數名的方法。因此當建立一個構造方法元素時,保證參數是按順序 排列的,並且數據類型也是肯定的。
<constructor >
<idArg column="id" javaType="int"/>
<arg column=」username」 javaType=」String」/>
</constructor>
剩餘的屬性和規則和固定的 id 和 result 元素是相同的。
2.3 關聯
<association property="author" column="blog_author_id" javaType=" Author">
<id property="id" column="author_id"/>
<result property="username" column="author_username"/>
</association>
關聯元素處理「有一個」類型的關係。好比,在咱們的示例中,一個博客有一個用戶。 關聯映射就工做於這種結果之上。你指定了目標屬性,來獲取值的列,屬性的 java 類型(很 多狀況下 MyBatis 能夠本身算出來),若是須要的話還有 jdbc 類型,若是你想覆蓋或獲取的結果值還須要類型控制器。
關聯中不一樣的是你須要告訴 MyBatis 如何加載關聯。MyBatis 在這方面會有兩種不一樣的 方式:
嵌套查詢:經過執行另一個 SQL 映射語句來返回預期的複雜類型。
嵌套結果:使用嵌套結果映射來處理重複的聯合結果的子集。
首先,讓咱們來查看這個元素的屬性。它和普通的只由 select 和resultMap 屬性的結果映射不一樣。
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 |
咱們在前面討論過默認的類型處理器 。使用這個屬性,你能夠覆 蓋默認的 類型處理器。這個屬性值是類的徹底限定名或者是一個類型處理器的實現, 或者是類型別名。 |
關聯的嵌套查詢
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> |
咱們有兩個查詢語句:一個來加載博客,另一個來加載做者,並且博客的結果映射描述了「selectAuthor」語句應該被用來加載它的 author 屬性。其餘全部的屬性將會被自動加載,假設它們的列和屬性名相匹配。
這種方式很簡單,可是對於大型數據集合和列表將不會表現很好。問題就是咱們熟知的 「N+1 查詢問題」。
因此還有另一種方法。
關聯的嵌套結果
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 元素扮演了很是重要的角色。應應該一般指定一個 或多個屬性,它們能夠用來惟一標識結果。實際上就是若是你離開她了,可是有一個嚴重的 性能問題時 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> |
2.4集合
<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 >
集合元素的做用幾乎和關聯是相同的。咱們來繼續上面的示例,一個博客只有一個做者。可是博客有不少文章。在博客類中, 這能夠由下面這樣的寫法來表示:
private List<Post> posts;
要映射嵌套結果集合到 List 中,咱們使用集合元素。就像關聯元素同樣,咱們能夠從 鏈接中使用嵌套查詢,或者嵌套結果。
集合的嵌套查詢
首先,讓咱們看看使用嵌套查詢來爲博客加載文章。
<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> |
這裏你應該注意不少東西,但大部分代碼和上面的關聯元素是很是類似的。首先,你應 該注意咱們 使用的是 集合元素 。而後要 注意那個 新的「of Ty pe 」屬性。這 個屬性用 來區分 JavaBean(或字段)屬性類型和集合包含的類型來講是很重要的。因此你能夠讀出下面這個 映射:
<collection property="posts" javaType=」ArrayList」 column="blog_id" ofType="Post" select=」selectPostsForBlog」/>
讀做:「在 Post 類型的 ArrayList 中的 posts 的集合。」
javaType 屬性是不須要的,由於 MyBatis 在不少狀況下會爲你算出來。因此你能夠縮短 寫法:
<collection property="posts" column="blog_id" ofType="Post" select=」selectPostsForBlog」/>
集合的嵌套結果
至此,你能夠猜想集合的嵌套結果是如何來工做的,由於它和關聯徹底相同,除了它應 用了一個「ofType 」屬性
<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> |
一樣,若是你引用更長的形式容許你的結果映射的更多重用,你可使用下面這個替代的映射:
<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> |