Mybatis结果映射——一对多

前言

我们知道 Mybatis 的结果映射,如果遇到实体类的属性是自定义,事情就不会那么简单。可能会有一对多,多对一,多对多的情况产生。本文讲解一下多对一的情况。

多对一,指的是 A 表的多行对应 B 表的某一行,就是 A 的多个实例对应 B 的一个实例。比如我们有一张博客表,表里记录了作者,题目,时间,阅读量等信息。还有一张读者表。表里记录了作者姓名、地址等信息。 Blog 表中的多条记录,对应了 Author 表中的一条记录,这就是多对一。

多对一的结果集映射需要用到 <association> 元素,来表示一个类中的属性是一个复杂类。

下面我们先建两张表,来具体描述一下多对一的关系。

数据库环境

我们有两个 pojo 类,一个是博客类 Blog.java,另一个是作者类 Author.java。
一个 Blog 类 “有一个” Author,该类有以下属性:

public class Blog {
 private String id;
 private String title;
 private Author author;
 private String create_time;
}

Blog 类对应的数据表为(我这里特意让类的属性名和数据表的列名不同,为的是做结果映射的时候看让大家看的更清楚):
可以看到博客的作者是由 author_id (数字)表示的,一共有 4 位作者,每位作者都发表了至少一篇博客。
在这里插入图片描述

Author 类有以下属性:

public class Author {
 private int id;
 private String name;
 private String address;
}

Author类对应的数据表为,这里记录的就不止是 Blog 表中出现的作者了,而是所有作者的信息,一共六位:
在这里插入图片描述

可以看到两张表是有关联的,可以通过作者的 id 建立联系,即 blog.author = author.id
在这里插入图片描述

多个 Blog 实体对应一个 Author 实体:

在这里插入图片描述

而且该问题复杂之处在哪里呢?在于 Blog 类的属性不全是基础类型,还包含了自定义的 Author 类,这使我们结果映射的时候要多做一些处理。

sql 操作

现在要求在 Blog 表中,传入作者 ID,查询储该作者写的所有博客的信息。
说到底,Mybatis 要操作的还是数据库,所以我们回归 MySQL,看看上面这个任务,用 SQL 怎么写。这个很简单,只需要执行:

select * from mybatis.blog where author_id = 315

就可以得到 author_id 为 315 的作者所有博客信息:
在这里插入图片描述
但是仅仅这样还不够,我想同时查出作者的信息,也就是在上面的结果后增加三列数据 id,name,address。这就涉及到多表查询了,两个表需要通过 blog.author_id = author.id 作为连接条件做查询。我有两种写法:
第一种写法——多表查询

左连接查询

select
    blog_id, blog_title, author_id, create_time, view_count, name, address
from Mybatis.Blog left join Mybatis.Author on Blog.author_id = Author.id
where Blog.author_id = 315; 

或者也可以这样查询

select blog_id, blog_title, author_id, create_time, view_count, name, address 
from mybatis.blog, mybatis.author 
where author_id = id and author_id = 315;

第二种写法——嵌套select查询(性能糟糕):

# 先查询从 blog 表中查询 author_id = 315 的数据
select * from mybatis.blog where author_id = 315;
# 再从 author 数据表中查询 id = 315 的数据
select * from mybatis.author where id = 315;
# 将数据组合成最终数据

二者的查询结果是一样的:
在这里插入图片描述

嵌套select查询

select * from blog inner join author on (blog.author_id = author.id and author.id = 315);

应该怎么在 Mybatis 中操作该 sql 语句呢?
先在 BlogMapper 中定义查询接口:

public interface BlogMapper {
    public List<Blog> queryBlogAndAuthorByAuthorID(int id);
}

再在 AuthorMapper 中定义查询接口:

public interface UserMapper {
    public User selectUser(int id);
}

之后要在 BlogMapper.xml 文件中定义 sql 语句:

    <select id="queryBlogAndAuthorByAuthorID" resultMap="BlogAuthor">
        select * from mybatis.blog where author_id = #{id}
    </select>
    <resultMap id="BlogAuthor" type="Blog">
        <result property="id" column="blog_id"></result>
        <result property="title" column="blog_title"></result>
        <result property="view" column="view_count"></result>
        <association property="author" column="author_id" javaType="Author" select="selectAuthorById">
            <result column="id" property="id" />
            <result column="name" property="name" />
            <result column="address" property="address" />
        </association>
    </resultMap>
    <select id="selectAuthorById" resultType="Author">
        select * from mybatis.author where id=#{id}
    </select>

之后调用接口就可以完成查询,结果正是我们想要的:
在这里插入图片描述

这种方式在大型数据集和大型数据表上表现不佳。该问题被称为 “N + 1” 查询问题:

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

在这个例子中:

  • 我们执行了一个单独的 SQL:“selectBlog”,先获得了一个Blog 列表 (“+1”)
  • 对于列表 Blog 返回的每条记录,又执行了 “selectAuthor” 来为每条 blog 记录加载 Author 的详细信息(“N”)
    在这里插入图片描述

来解释一下刚才发生了什么事。

我们有两个 select 查询语句:一个用来加载 Blog,一个用来加载 Author,而且博客的结果映射描述了应该使用 selectAuthorById 语句加载 它的 author 属性,就是这句:

 <association property="author" column="author_id" javaType="Author" select="selectAuthorById">

用到的几个 association 属性的描述如下:

属性描述
property属性名:列结果===》实体类的属性名
column列名:实体类的属性名===》列名
javaType该属性的 Java 类型
select加载复杂类型属性的映射语句的 ID

对于 Blog 类型,将 author 属性映射到了 Blog 数据表的 author_id 列中,由于 Author 对象不止有 author_id,这个属性,还有 name, address 属性,所以加载了复杂类型属性之后,还要在属性(Author)做结果映射:

        <association property="author" column="author_id" javaType="Author" select="selectAuthorById">
            <result column="id" property="id" />
            <result column="name" property="name" />
            <result column="address" property="address" />
        </association>

因为 Author 的列名和属性名匹配,可以被自动加载,故上述语句可以简写为:

 <association property="author" column="author_id" javaType="Author" select="selectAuthorById" />

扩展

假设我们现在要查询整个 Blog 表中的数据,每一行中增加作者的全部信息,也就是说,在 Blog 表后面增加作者数据三列数据 id, name, address。这回比起前面的需求,不要求搜索特定的作者了,只需要稍加改动即可。

先在 BlogMapper 中定义接口:

public interface BlogMapper {
    public List<Blog> queryBlog();
}

再在AuthorMapper 中定义接口:

public interface UserMapper {
    public User selectUser(int id);
}

之后在 BlogMapper 中定义 sql 语句:

    <select id="queryBlog" resultMap="BlogAuthor">
        select * from mybatis.blog
    </select>
    <resultMap id="BlogAuthor" type="Blog">
        <result property="id" column="blog_id"></result>
        <result property="title" column="blog_title"></result>
        <result property="view" column="view_count"></result>
        <association property="author" column="author_id" javaType="Author" select="selectAuthorById"></association>
    </resultMap>
    <select id="selectAuthorById" resultType="Author">
        select * from mybatis.author where id=#{id}
    </select>

查询结果如下:
在这里插入图片描述

关联的嵌套结果映射

我觉得这个 SQL 语句是比较符合直觉的,比较好写:

        select
            blog_id, blog_title, author_id, create_time, view_count, name, address
        from Mybatis.Blog left join Mybatis.Author on Blog.author_id = Author.id
        where Blog.author_id = 315; 

就是拿 Blog 表作为驱动表,用 Blog.author_id = Author.id 作为连接条件,找到 Blog.author_id = #{id} 的 Blog + Author 数据。怎么在 Mybatis 中完成上述操作呢?

将 Blog 表和 Author 表连接在一起,而不是执行一个独立的查询语句,就像这样:

    <select id="queryBlogAndAuthorByAuthorID" resultMap="BlogResult">
        select
            blog_id, blog_title, author_id, create_time, view_count, name, address
        from Mybatis.Blog left join Mybatis.Author on Blog.author_id = Author.id
        where Blog.author_id = #{id}
    </select>

之后结果映射:

    <resultMap id="BlogResult" type="Blog">
        <id property="id" column="blog_id" />
        <result property="title" column="blog_title" />
        <result property="create_time" column="create_time" />
        <result property="view" column="view_count" />
        <association property="author" column="author_id" javaType="Author" resultMap="authorResult"/>
    </resultMap>

    <resultMap id="authorResult" type="Author">
        <id property="id" column="author_id" />
        <id property="name" column="name"/>
        <id property="address" column="address"/>
    </resultMap>

调用接口即可完成查询:

在这里插入图片描述

这个结果映射很简单,就是将结果映射到 Blog 类型,再将 Blog 属性的复杂类型 Author 映射到 author 数据表中。使用 assicia tion 元素来映射:

<association property="author" column="author_id" javaType="Author" resultMap="authorResult"/>

association 中使用到了 resultMap 属性,再次做结果映射,把 author 的返回结果映射到 authorResult 中,而 authorResult 其实就是 Author 类和 author 表之间的映射:

    <resultMap id="authorResult" type="Author">
        <id property="id" column="author_id" />
        <id property="name" column="name"/>
        <id property="address" column="address"/>
    </resultMap>

该结果映射可以写为:

    <resultMap id="BlogResult" type="Blog">
        <id property="id" column="blog_id" />
        <result property="title" column="blog_title" />
        <result property="create_time" column="create_time" />
        <result property="view" column="view_count" />
        <association property="author" javaType="Author">
            <id property="id" column="author_id" />
            <id property="name" column="name"/>
            <id property="address" column="address"/>
        </association>
    </resultMap>

同理,上述的结果映射,如果使用下面的 SQL,查询效果是一样的:

    <select id="queryBlogAndAuthorByAuthorID" resultMap="BlogResult">
        select blog_id, blog_title, author_id, create_time, view_count, name, address 
        from Mybatis.Blog, Author 
        where Blog.author_id = Author.id and Blog.author_id = #{id}
    </select>

查询结果如下:
在这里插入图片描述

  • 4
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
MyBatis是一个流行的Java持久层框架,它可以用于数据库操作。在MyBatis中,一对多关系的映射可以通过使用嵌套查询或嵌套结果映射来实现。 如果你有两个实体类,比如一个"Department"类和一个"Employee"类,其中一个部门可以有多个员工,那么你可以在"Department"类中使用一个List或Set类型的属性来表示员工列表。在映射文件中,你可以使用嵌套查询或嵌套结果映射来获取和映射这些关系。 以下是一个简单的示例: 首先,创建Department类和Employee类,Department类中包含一个List属性来表示员工列表: ```java public class Department { private int id; private String name; private List<Employee> employees; // 省略getter和setter方法 } public class Employee { private int id; private String name; private int departmentId; // 省略getter和setter方法 } ``` 接下来,在MyBatis映射文件中定义相关的查询语句。使用嵌套查询时,可以在Department的映射文件中定义一个查询语句来获取对应部门的所有员工: ```xml <!-- DepartmentMapper.xml --> <select id="getDepartmentById" parameterType="int" resultType="Department"> SELECT * FROM department WHERE id = #{id} </select> <select id="getEmployeesByDepartmentId" parameterType="int" resultType="Employee"> SELECT * FROM employee WHERE department_id = #{id} </select> ``` 然后,使用嵌套结果映射来自动将查询结果映射到Department对象的employees属性: ```xml <!-- DepartmentMapper.xml --> <select id="getDepartmentById" parameterType="int" resultType="Department"> SELECT * FROM department WHERE id = #{id} </select> <!-- 使用嵌套结果映射 --> <resultMap id="departmentMap" type="Department"> <id property="id" column="id"/> <result property="name" column="name"/> <collection property="employees" ofType="Employee"> <id property="id" column="employee_id"/> <result property="name" column="employee_name"/> <result property="departmentId" column="department_id"/> </collection> </resultMap> ``` 最后,在你的Mapper接口中定义对应的方法来调用映射文件中的查询语句: ```java public interface DepartmentMapper { Department getDepartmentById(int id); } ``` 这样,当你调用getDepartmentById方法时,MyBatis会自动执行相关的查询语句,并将结果映射到Department对象的employees属性中,实现一对多关系的映射

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值