从 01 开始 从 01 开始
首页
  • 📚 计算机基础

    • 计算机简史
    • 数字电路
    • 计算机组成原理
    • 操作系统
    • Linux
    • 计算机网络
    • 数据库
    • 编程工具
    • 装机
  • 🎨 前端

    • Node
  • JavaSE
  • Java 高级
  • JavaEE

    • 构建、依赖管理
    • Ant
    • Maven
    • 日志框架
    • Junit
    • JDBC
    • XML-JSON
  • JavaWeb

    • 服务器软件
    • 环境管理和配置管理-科普篇
    • Servlet
  • Spring

    • Spring基础
  • 主流框架

    • Redis
    • Mybatis
    • Lucene
    • Elasticsearch
    • RabbitMQ
    • MyCat
    • Lombok
  • SpringMVC

    • SpringMVC 基础
  • SpringBoot

    • SpringBoot 基础
  • Windows 使用技巧
  • 手机相关技巧
  • 最全面的输入法教程
  • 最全面的浏览器教程
  • Office
  • 图片类工具
  • 效率类工具
  • 最全面的 RSS 教程
  • 码字工具
  • 各大平台
  • 校招
  • 五险一金
  • 职场规划
  • 关于离职
  • 杂谈
  • 自媒体
  • 📖 读书

    • 读书工具
    • 走进科学
  • 🌍 英语

    • 从零开始学英语
    • 英语兔的相关视频
    • Larry 想做技术大佬的相关视频
  • 🏛️ 政治

    • 新闻合订本
    • 反腐
    • GFW
    • 404 内容
    • 审查与自我审查
    • 互联网
    • 战争
    • 读书笔记
  • 💰 经济

    • 关于税
    • 理财
  • 💪 健身

    • 睡眠
    • 皮肤
    • 口腔健康
    • 学会呼吸
    • 健身日志
  • 🏠 其他

    • 驾驶技能
    • 租房与买房
    • 厨艺
  • 电影

    • 电影推荐
  • 电视剧
  • 漫画

    • 漫画软件
    • 漫画推荐
  • 游戏

    • Steam
    • 三国杀
    • 求生之路
  • 小说
  • 关于本站
  • 关于博主
  • 打赏
  • 网站动态
  • 友人帐
  • 从零开始搭建博客
  • 搭建邮件服务器
  • 本站分享
  • 🌈 生活

    • 2022
    • 2023
    • 2024
    • 2025
  • 📇 文章索引

    • 文章分类
    • 文章归档

晓林

程序猿,自由职业者,博主,英语爱好者,健身达人
首页
  • 📚 计算机基础

    • 计算机简史
    • 数字电路
    • 计算机组成原理
    • 操作系统
    • Linux
    • 计算机网络
    • 数据库
    • 编程工具
    • 装机
  • 🎨 前端

    • Node
  • JavaSE
  • Java 高级
  • JavaEE

    • 构建、依赖管理
    • Ant
    • Maven
    • 日志框架
    • Junit
    • JDBC
    • XML-JSON
  • JavaWeb

    • 服务器软件
    • 环境管理和配置管理-科普篇
    • Servlet
  • Spring

    • Spring基础
  • 主流框架

    • Redis
    • Mybatis
    • Lucene
    • Elasticsearch
    • RabbitMQ
    • MyCat
    • Lombok
  • SpringMVC

    • SpringMVC 基础
  • SpringBoot

    • SpringBoot 基础
  • Windows 使用技巧
  • 手机相关技巧
  • 最全面的输入法教程
  • 最全面的浏览器教程
  • Office
  • 图片类工具
  • 效率类工具
  • 最全面的 RSS 教程
  • 码字工具
  • 各大平台
  • 校招
  • 五险一金
  • 职场规划
  • 关于离职
  • 杂谈
  • 自媒体
  • 📖 读书

    • 读书工具
    • 走进科学
  • 🌍 英语

    • 从零开始学英语
    • 英语兔的相关视频
    • Larry 想做技术大佬的相关视频
  • 🏛️ 政治

    • 新闻合订本
    • 反腐
    • GFW
    • 404 内容
    • 审查与自我审查
    • 互联网
    • 战争
    • 读书笔记
  • 💰 经济

    • 关于税
    • 理财
  • 💪 健身

    • 睡眠
    • 皮肤
    • 口腔健康
    • 学会呼吸
    • 健身日志
  • 🏠 其他

    • 驾驶技能
    • 租房与买房
    • 厨艺
  • 电影

    • 电影推荐
  • 电视剧
  • 漫画

    • 漫画软件
    • 漫画推荐
  • 游戏

    • Steam
    • 三国杀
    • 求生之路
  • 小说
  • 关于本站
  • 关于博主
  • 打赏
  • 网站动态
  • 友人帐
  • 从零开始搭建博客
  • 搭建邮件服务器
  • 本站分享
  • 🌈 生活

    • 2022
    • 2023
    • 2024
    • 2025
  • 📇 文章索引

    • 文章分类
    • 文章归档
  • JavaSE

  • JavaSenior

  • JavaEE

  • JavaWeb

  • Spring

  • 主流框架

    • Redis

    • Mybatis

      • Mybatis 介绍
      • Mybatis 入门案例
      • Mybatis 入门案例-注解
      • Mybatis 入门案例-实现类
      • Mybatis 内部执行原理概述
      • 实现一个微型的 Mybatis-配置文件版
      • 实现一个微型的 Mybatis-注解版
      • Mybatis 实现 CRUD
      • Mybatis 中传递对象参数
      • Mybatis 中的列名和属性名的映射
      • Mybatis 实现 DAO 层的开发
      • Mybatis 实现类的执行过程-查询方法
      • properties 标签的使用及细节
      • typeAliases 标签和 package 标签
      • Mybatis 连接池和事务
      • Mybatis 与 JNDI
      • Mybatis 中的动态 SQL
      • Mybatis 多表查询
        • 表之间的关系
        • 示例 1:用户和账户
        • 实现方式 1:通过写 Account 的子类实现
        • 实现方式 2:建立实体类之间的关系
        • 一对多查询
        • 源码
      • Mybatis 中的多对多查询
      • Mybatis 的延迟加载
      • Mybatis 的缓存
      • Mybatis 的注解开发-CRUD
      • Mybatis 的注解开发-多表查询
    • Lucene

    • Elasticsearch

    • MQ

    • MyCat

    • Lombok

  • SpringMVC

  • SpringBoot

  • Java
  • 主流框架
  • Mybatis
2023-04-25
目录

Mybatis 多表查询

# 160.Mybatis 多表查询

之前我们都是查询单表,现在我们来讲下查询多表的情况 ‍

# 表之间的关系

表与表之间的关系有很多:

  • 一对多,

  • 多对一

  • 一对一

  • 多对多 ‍ 我们举几个生活中的例子:

  • 用户和订单就是一对多,订单和用户就是多对一,一个用户可以下多个订单,多个订单属于同一个用户

  • 人和身份证号就是一对一,一个人只能有一个身份证号,一个身份证号只能属于一个人

  • 老师和学生之间就是多对多,一个学生可以被多个老师教过,一个老师可以交多个学生

特例:如果拿出每一个订单,他都只能属于一个用户,所以 Mybatis 就把多对一看成了一对一。 ‍

# 示例 1:用户和账户

我们通过户和账户之间的关系,来学习 Mybatis 如何实现多表查询。

一个用户可以有多个账户,一个账户只能属于一个用户,多个账户也可以属于同一个用户。

步骤:

  1. 建立两张表:用户表,账户表,让用户表和账户表之间具备一对多的关系,并且在账户表有个外键,关联了用户的 ID。该步骤我们在一开始学习 Mybatis 的时候,已经创建过了,因此该步骤可以省略;
  2. 建立两个实体类:用户实体类和账户实体类,让用户和账户的实体类能体现出来一对多的关系
  3. 建立两个配置文件:用户的配置文件,账户的配置文件
  4. 实现配置:当我们查询用户时,可以同时得到用户下所包含的账户信息;当我们查询账户时,可以同时得到账户的所属用户信息 ‍

# 创建 Account 类

package com.peterjxl.domain;
import java.io.Serializable;
public class Account implements Serializable {
    private Integer id;
    private Integer uid;
    private Double money;
}
1
2
3
4
5
6
7

注:请读者自行生成 setter 和 getter 方法 ‍

# 创建 IAccountDao 接口

package com.peterjxl.dao;
import com.peterjxl.domain.Account;
import java.util.List;

public interface IAccountDao {
    List<Account> findAll();
}
1
2
3
4
5
6
7

‍

# 创建 IAccountDao.xml 文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.peterjxl.dao.IAccountDao">

    <select id="findAll" resultType="account">
        select * from account
    </select>
</mapper>
1
2
3
4
5
6
7
8
9
10

‍

# 创建测试类 AccountTest

该测试类和我们之前的测试类,几乎一样,可以复制后修改下:

package com.peterjxl.test;

import com.peterjxl.dao.IAccountDao;
import com.peterjxl.domain.Account;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class AccountTest {

    private InputStream in;
    private SqlSessionFactoryBuilder builder;
    private SqlSessionFactory factory;
    private SqlSession session;
    private IAccountDao accountDao;

    @Before
    public void init() throws IOException {
        // 1. 读取配置文件
        in = Resources.getResourceAsStream("SqlMapConfig.xml");

        // 2. 创建SqlSessionFactory工厂
        builder = new SqlSessionFactoryBuilder();
        factory = builder.build(in);

        // 3. 使用工厂生成SqlSession对象
        session = factory.openSession();

        // 4. 使用SqlSession创建Dao接口的代理对象
        accountDao = session.getMapper(IAccountDao.class);
    }

    @After
    public void destroy() throws IOException {
        // 6. 释放资源
        session.close();
        in.close();
    }

    @Test
    public void testFindAll(){
        List<Account> accounts = accountDao.findAll();
        for (Account account : accounts){
            System.out.println(account);
        }
    }

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57

‍ 此时我们运行下测试方法,应该是正常的; ‍

# SQL 准备

此时,我们就可以开始写多表查询的代码了。

首先,我们想要的效果是:查询所有账户,并获取到当前账户的所属用户信息(用户名和住址)。我们先写 SQL:

SELECT a.*, u.username, u.address
FROM account a, user u
WHERE u.id = a.UID
1
2
3

‍ 查询结果如下:

ID	UID	MONEY	username	address
1	46	1000	小七	        北京
2	45	1000	赵六	        北京
3	46	2000	小七	        北京
1
2
3
4

# 实现方式 1:通过写 Account 的子类实现

由于我们需要 Account 表的信息,并且还要用户名和住址,此时有一种方式就是,通过写一个 Account 类的子类,里面加上这两个成员变量,然后查询数据库并返回。

package com.peterjxl.domain;

public class AccountUser extends Account{
    private String username;
    private String address;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return super.toString() + "  AccountUser{" +
                "username='" + username + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

‍ 接口 IAccountDao 中新增方法:

/**
 * 查询所有账户,并带有用户名称和地址信息
 */
List<AccountUser> findAccountUser();
1
2
3
4

‍ 然后在 IUserDao.xml 中配置:

<!-- 查询所有账户,同时包含用户名和地址信息   -->
<select id="findAccountUser" resultType="accountUser">
    SELECT a.*, u.username, u.address
    FROM account a, user u
    WHERE u.id = a.UID
</select>
1
2
3
4
5
6

我们新建一个测试方法:

@Test
public void testFindAccountUser(){
    List<AccountUser> accounts = accountDao.findAccountUser();
    for (AccountUser account : accounts){
        System.out.println(account);
    }
}
1
2
3
4
5
6
7

‍ 运行结果:

Account{id=1, uid=46, money=1000.0} AccountUser{username='小七', address='北京'}
Account{id=2, uid=45, money=1000.0} AccountUser{username='赵六', address='北京'}
Account{id=3, uid=46, money=2000.0} AccountUser{username='小七', address='北京'}
1
2
3

‍ 可以看到能正常查询出来的。

为了实现多表查询,还得写一个子类,这太不方便,几乎不用,我们可以用其他方式来实现多表查询。 ‍

# 实现方式 2:建立实体类之间的关系

如何在类中体现类之间的关系呢?我们可以在 Account 中新建一个成员变量 user,因为 account 是 user 的从表

public class Account implements Serializable {

    private Integer id;
    private Integer uid;
    private Double money;
    private User user;
}
1
2
3
4
5
6
7

注:自行生成 getter 和 setter 。 ‍ 当我们在查询 account 的时候,我们希望将 user 的信息封装到 user 中;此时我们需要定义一个封装 account 和 user 的 resultMap,我们在 IAccountDao.xml 里新建一个:

<resultMap id="accountUserMap" type="account">
    <id property="id" column="id"/>
    <result property="uid" column="uid"/>
    <result property="money" column="money"/>
    <!--  建立一对一的关系映射,配置封装user的内容      -->
    <association property="user" column="uid" javaType="user">
        <id property="userId" column="id"/>
        <result property="userName" column="username"/>
        <result property="userAddress" column="address"/>
    </association>
</resultMap>
1
2
3
4
5
6
7
8
9
10
11

我们用 association 标签,表明一对一的映射,property 表明查询结果封装到 account 的哪个属性中, column 属性表面两表之间用什么字段关联,javatype 表明实体类对应的类名。然后标签里写要封装的 user 属性 ‍ 我们修改 findAll 标签中,查询的 SQL:

<select id="findAll" resultMap="accountUserMap">
    SELECT a.*, u.username, u.address
    FROM account a, user u
    WHERE u.id = a.UID
</select>
1
2
3
4
5

我们在测试方法中,加上打印 user 属性的语句:

@Test
public void testFindAll(){
    List<Account> accounts = accountDao.findAll();
    for (Account account : accounts){
        System.out.print(account);
        System.out.println(account.getUser());
    }
}
1
2
3
4
5
6
7
8

执行结果:

Account{id=1, uid=46, money=1000.0}User{userId=1, userName='小七', userBirthday=null, userSex='null', userAddress='北京'}
Account{id=2, uid=45, money=1000.0}User{userId=2, userName='赵六', userBirthday=null, userSex='null', userAddress='北京'}
Account{id=3, uid=46, money=2000.0}User{userId=3, userName='小七', userBirthday=null, userSex='null', userAddress='北京'}
1
2
3

‍ 可以看到能正常封装 userName 和 userAddress 属性 ‍

# 一对多查询

例如,我们想要在查询所有用户的时候,同时查询到用户下所有账户的信息;我们先定义 SQL

select u.*, a.ID aid, a.UID, a.MONEY
from user u LEFT JOIN account a
on u.id  = a.UID
1
2
3

我们需要在 User 类中添加一对多的映射,主表实体类应该包含从表实体类的集合引用;我们在 User 类中,新建一个成员变量 accounts

public class User implements Serializable {
    private Integer userId;
    private String userName;
    private Date userBirthday;
    private String userSex;
    private String userAddress;
    private List<Account> accounts;
}
1
2
3
4
5
6
7
8

注:自行生成 getter 和 setter ‍

<resultMap id="userAccountMap" type="user">
    <id property="userId" column="id"/>
    <result property="userName" column="userName"/>
    <result property="userAddress" column="address"/>
    <result property="userSex" column="sex"/>
    <result property="userBirthday" column="birthday"/>
    <!-- 配置user对象中account集合的映射 -->
    <collection property="accounts" ofType="account">
        <id property="id" column="aid"/>
        <result property="uid" column="uid"/>
        <result property="money" column="money"/>
    </collection>
</resultMap>
1
2
3
4
5
6
7
8
9
10
11
12
13

我们用 collection 属性表示查询一个集合出来,ofType 表示集合中元素的类型;然后里面写 account 表的列 ‍ 然后我们将 IUserDao.xml 的 findAll 标签,修改 resultMap:

<select id="findAll" resultMap="userAccountMap">
    select u.*, a.ID aid, a.UID, a.MONEY
    from user u LEFT JOIN account a
    on u.id  = a.UID
</select>
1
2
3
4
5

‍ 修改 MybatisTest 中的测试方法,增加打印 accounts 的语句:

@Test
public void helloMybatis() throws Exception{
    List<User> users = userDao.findAll();
    for(User user : users){
        System.out.print(user);
        System.out.println(user.getAccounts());
    }
}
1
2
3
4
5
6
7
8

运行结果:可以看到 45 和 46 的用户,能成功查询出来 Accout 信息,其他用户则没有

‍

‍

# 源码

本文所有代码已上传到了 GitHub (opens new window) 和 Gitee (opens new window) 上,并且创建了分支 demo14,读者可以通过切换分支来查看本文的示例代码。

上次更新: 2025/6/3 09:31:54
Mybatis 中的动态 SQL
Mybatis 中的多对多查询

← Mybatis 中的动态 SQL Mybatis 中的多对多查询→

最近更新
01
新闻合订本 2025-10
10-31
02
2025 年 10 月记
10-30
03
用 AI 批量优化思源笔记排版
10-15
更多文章>
Theme by Vdoing | Copyright © 2022-2025 | 粤 ICP 备 2022067627 号 -1 | 粤公网安备 44011302003646 号 | 点击查看十年之约
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式