MyBatis的建立基於這樣一個思想:數據庫並非您想怎樣就怎樣的。雖然咱們但願全部的數據庫遵照第三範式或BCNF(修正的第三範式),但它們不是。若是有一個數據庫可以完美映射到全部應用程序,也將是很是棒的,但也沒有。結果集映射就是MyBatis爲解決這些問題而提供的解決方案。例如,咱們如何映射下面這條語句? [html] view plaincopy <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> [html] view plaincopy <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。 è最佳實踐:逐步地生成resultMap,單元測試對此很是有幫助。若是您嘗試一會兒就生成像上面這樣巨大的resultMap,可能會出錯,而且工做起來很是吃力。從簡單地開始,再一步步地擴展,而且進行單元測試。使用框架開發有一個缺點,它們有時像是一個黑合。爲了確保達到您所預想的行爲,最好的方式就是進行單元測試。這對提交bugs也很是有用。 下面一部分將詳細說明每一個元素。 id、result id、result是最簡單的映射,id爲主鍵映射;result其餘基本數據庫表字段到實體類屬性的映射。 [html] view plaincopy <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 <構造方法> [html] view plaincopy <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(構造方法)元素支持這個。 [java] view plaincopy 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<關聯映射> [html] view plaincopy <!-- 映射關聯的對象 --> <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<聯合查詢> [html] view plaincopy <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讓你「連接」結果映射,來處理嵌套結果。例子會很容易來仿照,這個表格後面也有一個示例。 [html] view plaincopy <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> 注意這個聯合查詢,以及採起保護來確保全部結果被惟一併且清晰的名字來重命名。這使得映射很是簡單。如今咱們能夠映射這個結果: [html] view plaincopy <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結果映射能夠重用。然而,若是你不須要重用它的話,或者你僅僅引用你全部的結果映射合到一個單獨描述的結果映射中。你能夠嵌套結果映射。這裏給出使用這種方式的相同示例: [html] view plaincopy <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<集合> [html] view plaincopy <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中,咱們使用集合元素。就像關聯元素同樣,咱們能夠從鏈接中使用嵌套查詢,或者嵌套結果。 集合的嵌套查詢 首先,讓咱們看看使用嵌套查詢來爲博客加載文章。 [html] view plaincopy <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(或字段)屬性類型和集合包含的類型來講是很重要的。因此你能夠讀出下面這個映射: [html] view plaincopy <collection property="posts" javaType="ArrayList" column="id" ofType="Post" select="selectBlogPost" /> 讀做:「在Post類型的ArrayList中的posts的集合。」 集合的嵌套結果 [html] view plaincopy <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> 如今用文章映射集合映射博客,能夠簡單寫爲: [html] view plaincopy <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元素的重要性。 若是你引用更長的形式容許你的結果映射的更多重用,你可使用下面這個替代的映 [html] view plaincopy <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鑑別器 [html] view plaincopy <discriminator javaType="int" column="draft"> <case value="1" resultType="DraftPost"/> </discriminator> 有時一個單獨的數據庫查詢也許返回不少不一樣(可是但願有些關聯)數據類型的結果集。鑑別器元素就是被設計來處理這個狀況的,還有包括類的繼承層次結構。鑑別器很是容易理解,由於它的表現很像Java語言中的switch語句。 定義鑑別器指定了column和javaType屬性。列是MyBatis查找比較值的地方。JavaType是須要被用來保證等價測試的合適類型(儘管字符串在不少情形下都會有用)。好比: [html] view plaincopy <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類型的值。若是它匹配任何一個鑑別器的實例,那麼就使用這個實例指定的結果映射。 還有另一種語法來作簡潔的映射風格。好比: [html] view plaincopy <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>