以前咱們提到的映射,都是簡單的字段和對象屬性一對一,假設對象的屬性也是一個對象,即涉及到兩個表的關聯,此時應該如何進行映射處理?
先看兩張表,author 和 book:
業務上對應關係爲,一個做者能寫多本書,可是一本書只有一個做者。對應的Java類以下:
public class Book {
private long id;
private String name;
private int price;
private Author author;
//... getter and setter
}
public class Author {
private long id;
private String name;
private int age;
private List<Book> bookList;
//... getter and setter
}
一、association 關聯
如今咱們但願經過查詢獲得一個Book類,且該類中的author屬性要求同時獲取出來,這時候已經不是簡單的數據庫字段和對象屬性的一對一映射,而涉及到兩張表,此時咱們就要用到 association 關鍵字。
association 表示一個複雜類型的關聯,能夠將許多結果包裝成這種類型。它是 resultMap 中的標籤屬性,這意味着
當你須要使用嵌套查詢返回結果,那麼你的結果映射只能選擇 resultMap,而不能再使用 resultType。
1.1 method1
使用起來和resultMap的基本結構無異,因此如上咱們提到的查詢需求,在mapper中能夠這樣寫:
<mapper namespace="dulk.learn.mybatis.dao.BookDao">
<resultMap id="bookResultMap" type="dulk.learn.mybatis.pojo.Book">
<id property="id" column="id" />
<result property="name" column="name" />
<result property="price" column="price" />
<!--關聯屬性-->
<association property="author" javaType="dulk.learn.mybatis.pojo.Author">
<!--注:此處column應爲book中外鍵列名-->
<id property="id" column="author_id" />
<!--注:避免屬性重名,不然屬性值注入錯誤-->
<result property="name" column="authorName" />
<result property="age" column="authorAge" />
</association>
</resultMap>
<!--嵌套查詢,結果映射只能使用resultMap-->
<select id="findBookById" parameterType="long" resultMap="bookResultMap">
SELECT
b.*,
a.name AS 'authorName',
a.age AS 'authorAge'
FROM book b, author a
WHERE b.author_id = a.id
AND b.id = #{id}
</select>
</mapper>
能夠看到 association 最基本的兩個屬性:
- property - 關聯對象在類中的屬性名(即Author在Book類中的屬性名,author)
- javaType - 關聯對象的Java類型
而association中的結構,則和resultMap無異了,一樣是id和result,可是仍然有兩個須要注意的點:
- id中的column屬性,其值應該儘可能使用外鍵列名,主要是對於重名的處理,避免映射錯誤
- 一樣的,對於result中的column屬性的值,也要避免重名帶來的映射錯誤,如上例若 a.name 不採用別名 "authorName",則會錯誤地將 b.name 賦值給Author的name屬性
1.2 method2
以前有提到,說 association 中結構和resultMap無異,事實上咱們也能夠直接引用其餘的resultMap,以下(注意修改id別名):
<mapper namespace="dulk.learn.mybatis.dao.BookDao">
<!--author的resultMap-->
<resultMap id="authorResultMap" type="dulk.learn.mybatis.pojo.Author">
<id property="id" column="authorId"/>
<result property="name" column="authorName"/>
<result property="age" column="authorAge"/>
</resultMap>
<resultMap id="bookResultMap" type="dulk.learn.mybatis.pojo.Book">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="price" column="price"/>
<!--引用author的resultMap-->
<association property="author" resultMap="authorResultMap" />
</resultMap>
<!--注意這裏a.id的別名和authorResultMap中相對應-->
<select id="findBookById" parameterType="long" resultMap="bookResultMap">
SELECT
b.*,
a.id AS 'authorId',
a.name AS 'authorName',
a.age AS 'authorAge'
FROM book b, author a
WHERE b.author_id = a.id
AND b.id = #{id}
</select>
</mapper>
1.3 method3
最後,還有一種方式,就是咱們先查詢出author,再將其放到book中去,至關於查詢語句分爲兩次,只是最終結果交給MyBatis來幫咱們組裝,這種方式利用了 association 的 select 屬性,同時還須要另寫 author 的查詢sql,book 的查詢sql也能夠不用再聯表。這種方式至關於兩次查詢,性能和效率較低,並不提倡。如上例使用這樣的方式,則以下:
<mapper namespace="dulk.learn.mybatis.dao.BookDao">
<resultMap id="bookResultMap" type="dulk.learn.mybatis.pojo.Book">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="price" column="price"/>
<!--使用select屬性進行查詢關聯-->
<association property="author" column="author_id" javaType="dulk.learn.mybatis.pojo.Author" select="findAuthorById"/>
</resultMap>
<!--簡化了book的查詢語句,再也不須要與其餘表關聯-->
<select id="findBookById" parameterType="long" resultMap="bookResultMap">
SELECT b.*
FROM book b
WHERE b.id = #{id}
</select>
<!--新增了author表的查詢語句,將會被調用獲取結果並組裝給book-->
<select id="findAuthorById" parameterType="long" resultType="dulk.learn.mybatis.pojo.Author">
SELECT *
FROM author
WHERE id = #{id}
</select>
</mapper>
二、collection 集合
有了對 association 的認識,使用 collection 其實也就無非是依葫蘆畫瓢了,一樣只能是 resultMap,一樣須要注意列名重複的問題,一樣能夠引用resultMap或者使用select。下面索性直接看個例子吧,即獲取一個Author做者,其中包含屬性 List<Book>:
<mapper namespace="dulk.learn.mybatis.dao.AuthorDao">
<resultMap id="authorResultMap" type="dulk.learn.mybatis.pojo.Author">
<id property="id" column="id"/>
<result property="name" column="name" />
<result property="age" column="age" />
<!--使用collection屬性,ofType爲集合內元素的類型-->
<collection property="bookList" ofType="dulk.learn.mybatis.pojo.Book" columnPrefix="book_">
<id property="id" column="id"/>
<result property="name" column="name" />
<result property="price" column="price" />
</collection>
</resultMap>
<select id="findById" parameterType="long" resultMap="authorResultMap">
SELECT a.*, b.id AS 'book_id', b.name AS 'book_name', b.price AS 'book_price'
FROM author a, book b
WHERE a.id = b.author_id
AND a.id = #{authorId}
</select>
</mapper>
另外延伸一下關於避免字段重名的方式,如上例 select 中,列名的別名都增長了前綴 "book_",那麼在collection中進行映射描時,就有兩種方式:
- 第一種即 column 的值和列名徹底一致,如 column="book_id"
- 第二種也就是推薦的方式,在 collection 中使用屬性 columnPrefix 來定義統一前綴,在接下來的 column 中就能夠減小工做量了,如上例中 columnPrefix = "book_",column = "id",它們的效果等同於 column = "book_id"