Mybatis中的兩種級聯方式
最近在做一個基於SpringBoot+MybatisPlus博客系統的項目,在管理後臺需要列出所有文章,效果是這樣的:
注意紅色部分,查出文章的信息時,還需要查文章的分類和文章的標籤。這很容易想到需要使用Mybatis的級聯查詢,但是在寫mapper文件代碼的時候,想到級聯其實有兩種方式:
- 基於分層次查詢的
- 基於SQL表連接的
這樣說,大家可能會覺得雲裏霧裏的,啥叫分層次的,啥又叫SQL表連接的,這裏給出代碼,想必平時使用過Mybatis的小夥伴就會明白了。
- 基於分層次查詢的
<resultMap id="resultMap1" type="site.alanliang.geekblog.domain.Article">
<id property="id" column="id"/>
<result property="title" column="title"/>
<association property="category" column="category_id" select="site.alanliang.geekblog.mapper.ArticleMapper.selectCategoryById"/>
</resultMap>
<select id="selectCategoryById" resultType="site.alanliang.geekblog.domain.Category">
select id, name from t_category where id = #{id}
</select>
<select id="selectArticleById1" parameterType="long" resultMap="resultMap1">
select id, title, category_id from t_article where id = #{id}
</select>
- 基於SQL表連接的
<resultMap id="resultMap2" type="site.alanliang.geekblog.domain.Article">
<id property="id" column="id"/>
<result property="title" column="title"/>
<association property="category" javaType="site.alanliang.geekblog.domain.Category">
<id property="id" column="id"/>
<result property="name" column="name"/>
</association>
</resultMap>
<select id="selectArticleById2" parameterType="long" resultMap="resultMap2">
select ta.id, ta.title, tc.id, tc.name
from t_article ta
inner join t_category tc
on ta.category_id = tc.id
where ta.id = #{aid}
</select>
注意association和select標籤的區別。我平時比較粗心,根本沒在意它們的區別,但是這次做項目的時候再次碰到,這次才引起了我的注意,也激起了我的好奇心,究竟它們的區別在哪呢?
查過資料後,我終於明白了:
分層次查詢的方式是單表查詢,首先發送1條SQL查詢文章(Article)的信息,然後查詢文章的分類(category)時再發送1條SQL,也就是查詢1條文章記錄需要發送2條SQL。
基於SQL表連接的方式是多表查詢,在這裏也就是Article表和Category表連接查詢,只需要發送1條SQL。
我們做個測試,測試代碼如下:
@Autowired
private ArticleMapper articleMapper;
@Test
void selectArticleById1(){
Article article = articleMapper.selectArticleById1(7L);
System.out.println(article);
}
@Test
void selectArticleById2(){
Article article = articleMapper.selectArticleById2(7L);
System.out.println(article);
}
測試結果如下:
結果顯而易見。
過程分析
- 基於分層次查詢
我認爲的大致過程是這樣的。首先,測試中執行代碼:
Article article = articleMapper.selectArticleById1(7L);
Mybatis會找到相應mapper文件中的這部分代碼:
<select id="selectArticleById1" parameterType="long" resultMap="resultMap1">
select id, title, category_id from t_article where id = #{id}
</select>
Mybatis發送完這條SQL語句,查詢到字段id,title和category_id。將字段id和title映射至resultMap1中的相應屬性,category_id後面會用到(注意!SQL語句中不能缺少這個字段,否則將查不到對應的category)而resultMap1中代碼:
<resultMap id="resultMap1" type="site.alanliang.geekblog.domain.Article">
<id property="id" column="id"/>
<result property="title" column="title"/>
<association property="category" column="category_id" select="site.alanliang.geekblog.mapper.ArticleMapper.selectCategoryById"/>
</resultMap>
中可發現還需要映射的屬性有category,因此此時Mybatis會根據column屬性的值,也就是對應Article表中的字段名(外鍵)category_id,拿到之前查出來的 category_id(這裏是7),然後在把id值傳到select屬性對應的語句中:
<select id="selectCategoryById" resultType="site.alanliang.geekblog.domain.Category">
select id, name from t_category where id = #{id}
</select>
接着發送這條SQL語句,返回結果後映射至category對象中id和name屬性,最後完成所有屬性的映射。
- 基於SQL表連接
這個比較好理解,直接就是多表連接查詢。需要注意的是,當兩個表中存在字段名一樣的時候,需要給表起別名,我這裏Article和Category的主鍵名字都是id,所以分別起了別名ta和tc,否則沒有辦法映射,Mybatis會報錯。
以上結論均基於個人的理解和總結,如果有不當之處還望指正!