@
目錄
- 1 資料準備
- 1.2 實體類, 接口和XML
- 2 一對多映射
- 2.1 collection集合映射
- 2.1.1 建立結果實體類
- 2.1.2 建立結果集
- 2.1.3 建立對應的方法和XML
- 2.1.4 測試
- 2.1.5 結果合并原理
- 2.1.5.1 合并的依據
- 2.1.5.2 id 的作用
- 2.1.5.3 id 的作用驗證
- 2.1.5.4 建議
- 2.2 collection 嵌套查詢方式
- 2.2.1 建立結果實體類
- 2.2.2 建立結果集
- 2.2.3 建立對應的方法和XML
- 2.2.4 測試
- 2.1 collection集合映射
- 3 代碼
- 一起學 mybatis
在一對多的關系中, 主表的資料回對應關聯表中的多條資料。 是以, 查詢時就會查詢出多條結果, 是以, 向類似的情況我們會使用
List
來進行存儲關聯表中擷取到的資訊。
建立以下的名為 mybatis 的資料庫, 并在其下建立4個表。
在此就不貼出來建表的 SQL 語句了 , 感興趣的可以去我的 Github:mybatis-mapping 中擷取。
使用 mybatis-代碼生成器 生成相應的實體類, 接口和XML。
以上為生成的項目結構。
我們需要建立一個
BlogPostBO
,
public class BlogPostBO extends Blog {
private List<Post> posts;
public List<Post> getPosts() {
return posts;
}
public void setPosts(List<Post> posts) {
this.posts = posts;
}
}
該類繼承于
Blog
類, 并多了一個連結清單成員變量
posts
, 我們後續擷取到的釋出的文章都在此連結清單中。
剛開始時, 是這樣子建立的。
<resultMap id="BlogPostBO" type="com.homejim.mybatis.entity.BlogPostBO" extends="BaseResultMap">
<collection property="posts" columnPrefix="post_" ofType="com.homejim.mybatis.entity.Post">
<id column="id" jdbcType="INTEGER" property="id" />
<result column="blog_id" jdbcType="INTEGER" property="blogId" />
<result column="draft" jdbcType="INTEGER" property="draft" />
<result column="content" jdbcType="LONGVARCHAR" property="content" />
</collection>
</resultMap>
此處注意一個問題, 在 collection 中, 我們用的是 ofType 來表示
List
中的
Pojo
的屬性。而不是 type。
因為我們内部
Post
有對應的結果集, 可以引用另一個
Mapper
中的結果集, 就可以簡化變成下面這樣子:
<resultMap id="BlogPostBO" type="com.homejim.mybatis.entity.BlogPostBO" extends="BaseResultMap">
<collection property="posts" columnPrefix="post_" resultMap="com.homejim.mybatis.mapper.PostMapper.ResultMapWithBLOBs" />
</resultMap>
可以簡單了好多。
首先就是在對應的 Mapper 接口下建立方法:
/**
* 擷取部落格及其釋出的文章内容 一對多
* @return
*/
List<BlogPostBO> selectBlogAndPostList();
同時在 XML 中建立對應的 SQL 語句:
<select id="selectBlogAndPostList" resultMap="BlogPostBO">
SELECT
b.id,
b.title,
b.author_id,
p.id as post_id,
p.blog_id as post_blog_id,
p.draft as post_draft,
p.content as post_content
FROM blog b left join post p on p.blog_id=b.id
</select>
/**
* resultMap + collection 一對多映射
*/
@Test
public void testSelectBlogAndPostListByBlog() {
SqlSession sqlSession = sqlSessionFactory.openSession();
BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
List<BlogPostBO> blogPostBOs = blogMapper.selectBlogAndPostList();
sqlSession.close();
for (int i = 0; i < blogPostBOs.size(); i++) {
BlogPostBO blogPostBO = blogPostBOs.get(i);
System.out.println(blogPostBO.getTitle());
for (int j = 0; j < blogPostBO.getPosts().size(); j++) {
System.out.println(blogPostBO.getPosts().get(j).getContent());
}
System.out.println("=============這是對象分割線===============");
}
}
測試結果
可以看出, 已經擷取到了。
剛開始的時候, 我們擷取到的結果在資料庫中應該是這樣子的
那麼, 結果是怎麼變成下面這樣子的呢?
mybatis 在處理結果時, 會判斷對象是否相同, 如果相同則會将結果與之前的結果進行合并。
那麼, 結果是否相同是如何判斷的呢?
首先是通過 id的比較, 如果 id 沒有配置則比較每一個字段(此時, 隻要有一個字段不同的對象不同)。
在生成的 XML 中, 如果我們有主鍵, 一般會生成一個對應的 id 屬性。
而 id 屬性就可以用來判斷擷取到的資料是否屬于同一個對象。
在以上的例子中, 資料庫中查詢出來有三個 id=1, 則 mybatis 在處理結果時就可以知道這三條資料對應相同的對象, 進而将他們合并。
我們可以測試一下, 将 BlogMapper.xml 的 BaseResultMap 改成 title 為 id, 然後将資料庫改一下
那麼, 此時部落格 id=1 和 部落格 id=2 的 title 都為 “小明的部落格”, 那麼, 按照以上的推論, 此兩部落格的對象應該合二為一。
真相揭曉
小王的部落格消失了, 小明的部落格發表文章數量從 3 變為 5 , 因為合并了小王部落格釋出的文章數量。
建議盡量配置 id 的屬性, 如果沒有這個屬性, 則 mybatis 在進行結果合并時效率會低很多。
BlogPostBO
public class BlogPostBO extends Blog {
private List<Post> posts;
public List<Post> getPosts() {
return posts;
}
public void setPosts(List<Post> posts) {
this.posts = posts;
}
}
Blog
posts
結果集中多了一個 select 的屬性, 同時, 需要相應的指定 posts 和 column
<resultMap id="BlogPostCustom" type="com.homejim.mybatis.entity.BlogPostBO" extends="BaseResultMap">
<collection property="posts" column="{blogId=id}"
select="com.homejim.mybatis.mapper.PostMapper.selectPostByBlogId" fetchType="lazy" />
</resultMap>
重要說明:
- column 中, 需要填寫
的方式(如果隻有一個參數, 也可以直接填列名傳入參數的即可)。 如果是傳遞多個參數, 則是{屬性名=列名(别名)}
。{屬性名1=列名1, 屬性名2=列名2}
- select 屬性值
, 對應的 parameterType 是com.homejim.mybatis.mapper.PostMapper.selectPostByBlogId
(也可以不填)。java.util.Map
- fetchType 的屬性值 lazy(延遲加載, 還需要相應的配置) 或 eager。(更詳細的參考我上篇文章[mybatis-進階結果映射之一對一])
主方法
/**
* 擷取部落格及其釋出的文章内容 一對多:延遲加載
* @return
*/
List<BlogPostBO> selectBlogAndPostListLazy();
主方法對應的 XML
<select id="selectBlogAndPostListLazy" resultMap="BlogPostCustom">
SELECT
b.id,
b.title,
b.author_id
FROM blog b
</select>
嵌套方法
/**
* 根據部落格 id 擷取釋出的文章資訊
* @param blogId 部落格 id
* @return
*/
List<Post> selectPostByBlogId(int blogId);
嵌套方法對應 XML
<select id="selectPostByBlogId" parameterType="java.util.Map" resultMap="ResultMapWithBLOBs">
select
<include refid="Base_Column_List" />
,
<include refid="Blob_Column_List" />
from post
where blog_id = #{blogId,jdbcType=INTEGER}
</select>
/**
* resultMap + collection 一對多映射, 嵌套查詢
*/
@Test
public void testSelectBlogAndPostListByBlogLazy() {
SqlSession sqlSession = sqlSessionFactory.openSession();
BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
List<BlogPostBO> blogPostBOs = blogMapper.selectBlogAndPostListLazy();
for (int i = 0; i < blogPostBOs.size(); i++) {
BlogPostBO blogPostBO = blogPostBOs.get(i);
System.out.println(blogPostBO.getTitle());
List<Post> posts = blogPostBO.getPosts();
for (int j = 0; j < posts.size(); j++) {
System.out.println(blogPostBO.getPosts().get(j).getContent());
}
System.out.println("=============這是對象分割線===============");
}
}
可以看到, 隻有在需要使用對象的時候, 才會進行延遲加載。
我的 Github:mybatis-mapping
你想不想來學習 mybatis? 學習其使用和源碼呢?那麼, 在部落格園關注我吧!!
我自己打算把這個源碼系列更新完畢, 同時會更新相應的注釋。快去 star 吧!!
mybatis最新源碼和注釋

作者:阿進的寫字台
出處:https://www.cnblogs.com/homejim/
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。