【MyBatis】学习笔记011--resultMap高级结果映射之多对一、一对多

MyBatis 创建时的一个思想是:数据库不可能永远是你所想或所需的那个样子。 我们希望每个数据库都具备良好的第三范式或 BCNF 范式,可惜它们并不都是那样。 如果能有一种数据库映射模式,完美适配所有的应用程序,那就太好了,但可惜也没有。 而 ResultMap 就是 MyBatis 对这个问题的答案。

现实中,我们可能会遇到很多极为复杂的查询需求,像查询关系中的多对一、一对多,SQL语句好写,在后台逻辑中,如何去存储它们,可能没有那么容易做到,MyBatis的出现,为我们解决了这一问题。下面我们就来看如何做到理想中的查询。

前提

假设我们拥有两张表blog(博客表)、author(作者表)

bolg
字段 bolg_id bolg_title author_id
author
字段 author_id author_username

同时,我们的实体类如下

public Blog{
  private int blog_id;
  private String blog_title;
  private Author author;  
}
public Author{
  private int author_id;
private String author_name;
private List<Blog> blogs; }

需求1--多对一

需求:查询bolg及其作者author

1.1 方式一

<resultMap id="blogResult" type="Blog">
  <association property="author" column="author_id" javaType="Author" select="selectAuthor"/>
</resultMap>

<select id="selectBlog" resultMap="blogResult">
  SELECT * FROM BLOG WHERE ID = #{id}
</select>

<select id="selectAuthor" resultType="Author">
  SELECT * FROM AUTHOR WHERE ID = #{id}
</select>

就是这么简单。我们有两个 select 查询语句:一个用来加载博客(Blog),另外一个用来加载作者(Author),而且博客的结果映射描述了应该使用 selectAuthor 语句加载它的 author 属性。

其它所有的属性将会被自动加载,只要它们的列名和属性名相匹配。

这种方式虽然很简单,但在大型数据集或大型数据表上表现不佳。这个问题被称为“N+1 查询问题”。 概括地讲,N+1 查询问题是这样子的:

  • 你执行了一个单独的 SQL 语句来获取结果的一个列表(就是“+1”)。
  • 对列表返回的每条记录,你执行一个 select 查询语句来为每条记录加载详细信息(就是“N”)。

这个问题会导致成百上千的 SQL 语句被执行。有时候,我们不希望产生这样的后果。

好消息是,MyBatis 能够对这样的查询进行延迟加载,因此可以将大量语句同时运行的开销分散开来。 然而,如果你加载记录列表之后立刻就遍历列表以获取嵌套的数据,就会触发所有的延迟加载查询,性能可能会变得很糟糕。

所以还有另外一种方法。

1.2 方式二

<select id="selectBlog" resultMap="blogResult">
  select
    B.blog_id            as blog_id,
    B.blog_title         as blog_title,
    B.author_id     as blog_author_id,
    A.author_id            as author_id,
    A.author_username      as author_username,
  from Blog B left outer join Author A on B.author_id = A.author_id
  where B.id = #{id}
</select>

<resultMap id="blogResult" type="Blog">
  <id property="blog_id" column="blog_id" />
  <result property="blog_title" column="blog_title"/>
  <association property="author" column="author_id" javaType="Author" resultMap="authorResult"/>
</resultMap>

<resultMap id="authorResult" type="Author">
  <id property="author_id" column="author_id"/>
  <result property="author_username" column="author_username"/>
</resultMap>

以上是使用 2个resultMap+1个select标签完成的,如果觉得繁琐、混乱,可简化成如下方式

<select id="selectBlog" resultMap="blogResult">
  select
    B.blog_id            as blog_id,
    B.blog_title         as blog_title,
    B.author_id     as blog_author_id,
    A.author_id            as author_id,
    A.author_username      as author_username,
  from Blog B left outer join Author A on B.author_id = A.author_id
  where B.id = #{id}
</select>

<resultMap id="blogResult" type="Blog">
  <id property="blog_id" column="blog_id" />
  <result property="blog_title" column="blog_title"/>
  <association property="author" javaType="Author">
    <id property="author_id" column="author_id"/>
    <result property="author_username" column="author_username"/>
  </association>
</resultMap>

需求2--一对多

需求:查询作者author及其所有博客

类似于需求1,一对多也有两种方式

2.1 方式1

<select id="selectAuthor" resultMap="authorResult" parameterType="Integer">
  select * from author where author_id = #{id}
</select>
<select id="selectBlog" parameterType="Integer" resultType="Blog">
  select * from blog where author_id = #{id}
</select>

<resultMap id="authorResult" type="author">
  <id property="author_id" column="author_id" />
  <result property="author_name" column="author_name" />
  <collection property="blogs" javaType="ArrayList" ofType="Blog" column="author_id" select="selectBlog"/>
</resultMap>

2.2 方式2

<select id="selectAuthor" resultMap="authorResult" parameterType="Integer">
  select A.author_id as authorId, A.author_username as authorUserName,B.blog_id as blogId,B.blog_title as blogTitle
  from blog as B,author as A
on A.author_id = B.author_id where A.author_id = #{id}
</select> <resultMap id="authorResult" type="Author"> <id property="author_id" column="authorId"/> <result property="author_name" column="authorUserName"/> <collection property="blogs" ofType="Blog" /> <id property="blog_id" column="blogId"/> <result property="blog_title" column="blogTitle"/> </collection> </resultMap>

同样的,推荐使用方式2.

 总结

 

 

posted @ 2021-01-28 17:13  AirCL  阅读(129)  评论(0编辑  收藏  举报