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会报错。
以上结论均基于个人的理解和总结,如果有不当之处还望指正!