从01开始 从01开始
首页
  • 计算机科学导论
  • 数字电路
  • 计算机组成原理

    • 计算机组成原理-北大网课
  • 操作系统
  • Linux
  • Docker
  • 计算机网络
  • 计算机常识
  • Git
  • JavaSE
  • Java高级
  • JavaEE

    • Ant
    • Maven
    • Log4j
    • Junit
    • JDBC
    • XML-JSON
  • JavaWeb

    • 服务器软件
    • Servlet
  • Spring
  • 主流框架

    • Redis
    • Mybatis
    • Lucene
    • Elasticsearch
    • RabbitMQ
    • MyCat
    • Lombok
  • SpringMVC
  • SpringBoot
  • 学习网课的心得
  • 输入法
  • 节假日TodoList
  • 其他
  • 关于本站
  • 网站日记
  • 友人帐
  • 如何搭建一个博客
GitHub (opens new window)

peterjxl

人生如逆旅,我亦是行人
首页
  • 计算机科学导论
  • 数字电路
  • 计算机组成原理

    • 计算机组成原理-北大网课
  • 操作系统
  • Linux
  • Docker
  • 计算机网络
  • 计算机常识
  • Git
  • JavaSE
  • Java高级
  • JavaEE

    • Ant
    • Maven
    • Log4j
    • Junit
    • JDBC
    • XML-JSON
  • JavaWeb

    • 服务器软件
    • Servlet
  • Spring
  • 主流框架

    • Redis
    • Mybatis
    • Lucene
    • Elasticsearch
    • RabbitMQ
    • MyCat
    • Lombok
  • SpringMVC
  • SpringBoot
  • 学习网课的心得
  • 输入法
  • 节假日TodoList
  • 其他
  • 关于本站
  • 网站日记
  • 友人帐
  • 如何搭建一个博客
GitHub (opens new window)
  • 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的注解开发-多表查询
      • Mybatis
    • Lucene

    • Elasticsearch

    • MQ

    • MyCat

    • Lombok

    • 主流框架
  • SpringMVC

  • SpringBoot

  • Java并发

  • Java源码

  • JVM

  • 韩顺平

  • Java
  • 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,读者可以通过切换分支来查看本文的示例代码。

在GitHub上编辑此页 (opens new window)
上次更新: 2023/5/16 10:04:12
Mybatis中的动态SQL
Mybatis中的多对多查询

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

Theme by Vdoing | Copyright © 2022-2023 粤ICP备2022067627号-1 粤公网安备 44011302003646号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式