【MyBatis】学习笔记011--resultMap高级结果映射之多对一、一对多
MyBatis 创建时的一个思想是:数据库不可能永远是你所想或所需的那个样子。 我们希望每个数据库都具备良好的第三范式或 BCNF 范式,可惜它们并不都是那样。 如果能有一种数据库映射模式,完美适配所有的应用程序,那就太好了,但可惜也没有。 而 ResultMap 就是 MyBatis 对这个问题的答案。
现实中,我们可能会遇到很多极为复杂的查询需求,像查询关系中的多对一、一对多,SQL语句好写,在后台逻辑中,如何去存储它们,可能没有那么容易做到,MyBatis的出现,为我们解决了这一问题。下面我们就来看如何做到理想中的查询。
前提
假设我们拥有两张表blog(博客表)、author(作者表)
字段 | bolg_id | bolg_title | author_id |
字段 | 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.
总结