千字文带你入门-MyBatis多表查询

MyBatis多表操作

经过了 MyBatis 基本增删改查的学习,而在实际的项目中,我们往往会接触到多表的操作,什么是多表呢, 在实际生活中,每个实体之间往往是存在关系的,而我们的项目却是要依赖数据库将这些实体之间的关系串联起来,从而实现我们的业务,所以这部分,我们着重讲解如何使用 MyBatis 框架处理多张数据表之间的联系,帮助我们更加理解数据库的映射关系

(一) 表间关系

A:一对多

  • 用户和订单/理财产品

    • 一个用户可以买好几个批次的理财产品
  • 部门和员工
    • 一个部门可以有很多员工

B:多对一

  • 订单和用户

    • 多个订单属于同一个用户

C:多对多

  • 学生选课和学生

    • 一个学生可以选择多门课,一门课可以被多个学生选择

D:一对一

  • 身份证、护照等证件

    • 一个证件只能属于一个人

可以看到,第二章内容我们直接进入了业务表的内容,而由于前几篇文章的铺垫,我将User的相关信息都没有讲解,缺失的内容只有用户实体类,以及对应 XML 映射文件,这个非常简单 以及对应测试类

(二) 根据业务创建表

文章中我们使用用户和账户之间的账户的关系,即:

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

首先需要建立两张表:用户表和账户表

  • 让两者分别具备一对多的关系,我们需要在账户表中添加外键

User表

CREATE TABLE USER (
 `id`           INT(11)NOT NULL AUTO_INCREMENT,
 `username`     VARCHAR(32) NOT NULL COMMENT '用户名',
 `telephone`    VARCHAR(11) NOT NULL COMMENT '手机',
 `birthday`     DATETIME DEFAULT NULL COMMENT '生日',
 `gender`       CHAR(1) DEFAULT NULL COMMENT '性别',
 `address`      VARCHAR(256) DEFAULT NULL COMMENT '地址',
  PRIMARY KEY  (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;

Account表

CREATE TABLE `account` (
  `ID` int(11) NOT NULL COMMENT '编号',
  `UID` int(11) default NULL COMMENT '用户编号',
  `MONEY` double default NULL COMMENT '金额',
  PRIMARY KEY  (`ID`),
  KEY `FK_Reference_8` (`UID`),
  CONSTRAINT `FK_Reference_8` FOREIGN KEY (`UID`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

(三) 账户表-单表查询

首先创建其对应Account实体类

public class Account implements Serializable {
    private Integer id;
    private Integer uid;
    private Double money;
    ......对应 get set 方法
}

在其 AccountMappre 接口中增加查询所有的方法

public interface AccountMapper {

    /**
     * 查询所有账户
     * @return
     */
    List<Account> findAll();
}

增加其映射文件,注:省略头部的一些引入代码

<mapper namespace="cn.ideal.mapper.AccountMapper">
    <!-- 根据查询所有用户 -->
    <select id="findAll" resultType="Account">
        select * from account
    </select>
</mapper>

还是要再多啰嗦一句,resultType="Account" 这里是因为我们在主配置文件中已经,对omain中类都起了别名,所以可以直接用包下的类名,如果不了解的朋友,使用全类名也是一样的

测试一下:

/**
 * 测试查询所有
 */
@Test
public void testFindAll(){
    List<Account> accounts = accountMapper.findAll();
    for (Account account : accounts){
        System.out.println(account);
    }
}

看一下效果:

(四) Account 一对一查询

如何查询到 Acount 中信息的同时,根据用户的 id 值将对应的数据显示出来,这其实主要就是需要改变 SQL 的写法,我们在本地的 MySQL中先试一试

SELECT FROM account a,user u WHERE u.id=a.uid;

执行结果

结果出来了,但是 user 表中的 id 属性由于和 account 表中的 id 属性名称是一致的,所以自动起了别名,更好的做法是,我们自己设置其对应的别名

SELECT u.*,a.id as aid,a.uid,a.money FROM account a,user u WHERE u.id=a.uid;

这样看起来就条理了许多

到了这一步,我们就可以在代码中实现这样的功能了,即通过查询账户信息,同时查询出对应的用户信息,那由于注册时间,男女等信息,我并不想要,怎么办呢?我们可以再加一点约束,用户的信息只显示名称和地址两个字段

A:创建子类方式(不算太常用)

(1) 修改 Account 接口

/**
 * 查询所有账户,并且带有用户名称和地址信息
 * @return
 */
List<UserAccount> findAllAccount();

大家可能注意到我们返回的 List 类型为 UserAccount,这是为什么呢?

既然我们想返回的信息中,需要包含两个表中的信息,似乎我们并没有一个实体可以承载这么多信息,所以我们创建一个 UserAccount 类

(2) 创建 UserAccount 类

public class UserAccount extends Account {
    private String username;
    private String address;
    ......对应 get set 方法

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

说明:由于我们只需要显示 名称 和 地址 这两个字段,所以只需要创建 username 和 address 两个字段就可以了,而继承 Account 可以方便我们调用输出查询到账户中的信息

(3) 修改 AccountMapper.xml

<select id="findAllAccount" resultType="UserAccount">
        select a.*,u.username,u.address from account a , user u where u.id = a.uid;
    </select>

(4) 测试代码

/**
 * 查询所有账户,并且带有用户名称和地址信息
 * @return
 */
@Test
public void testFindAllAccount(){
    List<UserAccount> uas = accountMapper.findAllAccount();
    for (UserAccount ua : uas ){
        System.out.println(ua);
    }
}

(5) 执行效果

B:建立实体类关系方式(推荐)

(1) 修改 Account 接口

/**
 * 查询所有账户
 * @return
 */
List<Account> findAll();

(2) 修改 Account 类

在 Account 类中需要增加一个User对象的引用,这也就是对应着我们的 user 主表

//从表实体应该包含一个主表实体的对象引用
private User user;

public User getUser() {
    return user;
}

public void setUser(User user) {
    this.user = user;
}

(3) 修改 AccountMapper.xml

<!-- 定义封装 Account和User 的resultMap -->
<resultMap id="userAccountMap" type="Account">
    <id property="id" column="aid"></id>
    <result property="uid" column="uid"></result>
    <result property="money" column="money"></result>
    <!-- 配置封装 User 的内容 -->
    <association property="user" column="uid" javaType="User">
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="telephone" column="telephone"></result>
        <result property="birthday" column="birthday"></result>
        <result property="gender" column="gender"></result>
        <result property="address" column="address"></result>
     </association>
</resultMap>

<!-- 根据查询所有用户 -->
 <select id="findAll" resultMap="userAccountMap">
    SELECT u.*,a.id AS aid,a.uid,a.money FROM account a,user u WHERE u.id = a.uid;
</select>

说明:由于我们想要返回的结果为多个值,是没有一个完全与返回结果值一一匹配的封装类去接收的,所以我们可以使用MyBatis 提供的 resultMap 进行接收结果数据,它会在列名和 Java 包装类属性名之间创建映射关系,这篇文章的重心还是放在表的操作上,关于这个问题,以后可以专门写篇文章进行说明,如果对这部分不清楚的朋友,可以自己查一下这些标签的意义,实际上不会太过于复杂的

(4) 测试代码

/**
 * 测试查询所有
 */
@Test
public void testFindAll(){
    List<Account> accounts = accountMapper.findAll();
    for (Account account : accounts){
        System.out.println("--------------------------");
        System.out.println(account);
        System.out.println(account.getUser());
    }
}

(5) 执行效果

(五) User 一对多查询

(1) 修改 UserMapper 接口

/**
 * 查询所有用户信息,同时显示出该用户下的所有账户
 *
 * @return
 */
List<User> findAll();

(2) 修改 User 类

在 Java 类中应该添加一个集合成员,类型为 Account,方便我们承载账户的信息

//一对多关系映射,主表实体应该包含从表实体的集合引用
private List<Account> accounts;

public List<Account> getAccounts() {
    return accounts;
}

public void setAccounts(List<Account> accounts) {
    this.accounts = accounts;
}

(3) 修改 AccountMapper.xml

<!-- 定义User的resultMap-->
<resultMap id="userAccountMap" type="User">
    <id property="id" column="id"></id>
    <result property="username" column="username"></result>
    <result property="telephone" column="telephone"></result>
    <result property="birthday" column="birthday"></result>
    <result property="gender" column="gender"></result>
    <result property="address" column="address"></result>
    <collection property="accounts" ofType="account">
        <id property="id" column="aid"></id>
        <result property="uid" column="uid"></result>
        <result property="money" column="money"></result>
    </collection>
</resultMap>

<!-- 根据查询所有用户 -->
<select id="findAll" resultMap="userAccountMap">
   SELECT * FROM user u LEFT OUTER JOIN account a on u.id = a.uid;
</select>

注:LEFT OUTER JOIN :左外连接,可以将左表的数据全部显示出来

(4) 测试代码

/**
 * 测试查询所有
 */
@Test
public void testFindAll() {
    List<User> users= userMapper.findAll();
    for (User user : users) {
        System.out.println("--------------------------");
        System.out.println(user);
        System.out.println(user.getAccounts());
    }
}

(5) 执行效果

可以看到,所有用户信息被打印了出来(上图只截取了前面的部分),并且在用户下存在所有账户的信息也被打印了出来

(六) 多对多操作

前面我们看完了,用户以及账户之间一对多的关系,下面我们来研究一下多对多的情况,这种,情况会麻烦一些,例如我们举个例子:用户以及职位之间的关系

  • 一个用户可以有多个职位,而一个职位也可以属于多个用户

但是如何将两个表连接起来呢?这就需要一个中间表,用来使得两个表之间产生关系

首先创建一个职位表

CREATE TABLE `role` (
  `ID` int(11) NOT NULL COMMENT '编号',
  `ROLE_NAME` varchar(30) default NULL COMMENT '职位',
  `ROLE_DESC` varchar(60) default NULL COMMENT '描述',
  PRIMARY KEY  (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert  into `role`(`ID`,`ROLE_NAME`,`ROLE_DESC`) values (1,'班主任','管理整个班'),(2,'院长','管理整个学院'),(3,'校长','管理整个学校');

接着我们创建中间表

CREATE TABLE `user_role` (
  `UID` int(11) NOT NULL COMMENT '用户编号',
  `RID` int(11) NOT NULL COMMENT '职位编号',
  PRIMARY KEY  (`UID`,`RID`),
  KEY `FK_Reference_10` (`RID`),
  CONSTRAINT `FK_Reference_10` FOREIGN KEY (`RID`) REFERENCES `role` (`ID`),
  CONSTRAINT `FK_Reference_9` FOREIGN KEY (`UID`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert  into `user_role`(`UID`,`RID`) values (12,1),(16,1),(12,2);

至于用户表,我们依旧沿用前面的 user 的一套

A:查询所有职位信息

(1) 创建实体

public class Role implements Serializable {
    private Integer roleId;
    private String roleName;
    private String roleDesc;
    ...... 省略对应 get set toString 方法
}

(2) 创建接口并增加方法

public interface RoleMapper {
    /**
     * 查询所有职位
     * @return
     */
    List<Role> findAll();
}

(3) 创建 RoleMapper.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="cn.ideal.mapper.RoleMapper">

    <!-- 定义Role的resultMap-->
    <resultMap id="roleMap" type="Role">
        <id property="roleId" column="id"></id>
        <result property="roleName" column="role_name"></result>
        <result property="roleDesc" column="role_desc"></result>
    </resultMap>

    <!-- 根据查询所有用户 -->
    <select id="findAll" resultMap="roleMap">
       SELECT * FROM role
    </select>

</mapper>

需要特别注意的是:column中的值是数据库中字段名,而property中的值是JavaBean中的对应成员变量,由于两者的名字并不是相同的,所以请注意区分

(4) 测试代码

@Test
public void testFindAll(){
    List<Role> roles = roleMapper.findAll();
    for (Role role : roles){
        System.out.println("-----------------------");
        System.out.println(role);
     }
}

(5) 执行效果

B:查询角色获取对应职位

(1) 修改 Role 类

在 Role 实体类中增加 User 类型的 List集合

//多对多关系映射,一个职位可以拥有多个用户
private List<User> users;

public List<User> getUsers() {
    return users;
}

public void setUsers(List<User> users) {
    this.users = users;
}

接口方法我们仍然用前面创建的,findAll 方法

(2) 修改 RoleMapper.xml

在这部分,毫无疑问,需要创建 Role 的 resultMap ,还要一部分,就是对应 SQL 语句的编写

SQL语句的编写我们需要简单的分析一下,首先看一下,三张表之间的关系

中间表通过UID RID两个字段分别连接起了 user 和 role 两张表

先通过 role 表中的 id 找到 中间表的 rid 然后通过 rid 对应的 uid值 找到 user 表中的 id 值,从而获取到对应的用户信息

这个时候我们需要两个左外连接,xml 代码如下

<mapper namespace="cn.ideal.mapper.RoleMapper">
    <!-- 定义Role的resultMap-->
    <resultMap id="roleMap" type="Role">
        <id property="roleId" column="rid"></id>
        <result property="roleName" column="role_name"></result>
        <result property="roleDesc" column="role_desc"></result>
        <collection property="users" ofType="User">
            <id property="id" column="id"></id>
            <result property="username" column="username"></result>
            <result property="telephone" column="telephone"></result>
            <result property="birthday" column="birthday"></result>
            <result property="gender" column="gender"></result>
            <result property="address" column="address"></result>
        </collection>
    </resultMap>

    <!-- 根据查询所有用户 -->
    <select id="findAll" resultMap="roleMap">
       SELECT u.*,r.id AS rid,r.role_name,r.role_desc FROM role r
       LEFT OUTER JOIN user_role ur ON r.id = ur.rid
       LEFT OUTER JOIN user u ON u.id = ur.uid
    </select>
</mapper>

(3) 测试代码

@Test
public void testFindAll(){
    List<Role> roles = roleMapper.findAll();
    for (Role role : roles){
        System.out.println("---------------------");
        System.out.println(role);
        System.out.println(role.getUsers());
    }
}

(4) 执行效果

C:查询职位获取对应用户

(1) 修改接口方法

public interface UserMapper {
    /**
     * 查询所有用户信息,同时显示出该用户下的所有账户
     *
     * @return
     */
    List<User> findAll();

}

(2) 修改 User 实体

这是多对多的关系映射,一个用户可以具备多个角色

private List<Role> roles;

public List<Role> getRoles() {
    return roles;
}

public void setRoles(List<Role> roles) {
    this.roles = roles;
}

(3) 修改 RoleMapper.xml

<mapper namespace="cn.ideal.mapper.RoleMapper">
    <!-- 定义Role的resultMap-->
    <resultMap id="roleMap" type="Role">
        <id property="roleId" column="rid"></id>
        <result property="roleName" column="role_name"></result>
        <result property="roleDesc" column="role_desc"></result>
        <collection property="users" ofType="User">
            <id property="id" column="id"></id>
            <result property="username" column="username"></result>
            <result property="telephone" column="telephone"></result>
            <result property="birthday" column="birthday"></result>
            <result property="gender" column="gender"></result>
            <result property="address" column="address"></result>
        </collection>
    </resultMap>

    <!-- 根据查询所有用户 -->
    <select id="findAll" resultMap="roleMap">
       SELECT u.*,r.id AS rid,r.role_name,r.role_desc FROM role r
       LEFT OUTER JOIN user_role ur ON r.id = ur.rid
       LEFT OUTER JOIN user u ON u.id = ur.uid
    </select>
</mapper>

(4) 测试代码

@Test
public void testFindAll(){
    List<User> users = userMapper.findAll();
    for (User user : users){
        System.out.println("---------------------");
        System.out.println(user);
        System.out.println(user.getRoles());
    }
}

(5) 执行效果

结尾

如果文章中有什么不足,欢迎大家留言交流,感谢朋友们的支持!

如果能帮到你的话,那就来关注我吧!如果您更喜欢微信文章的阅读方式,可以关注我的公众号

在这里的我们素不相识,却都在为了自己的梦而努力 ?

一个坚持推送原创开发技术文章的公众号:理想二旬不止

原文地址:https://www.cnblogs.com/ideal-20/p/12286422.html

时间: 2024-08-29 18:25:21

千字文带你入门-MyBatis多表查询的相关文章

Mybatis关联表查询_5

使用Mybatis实现关联查询,分为一对一和一对多两种情况,最后并对ResultMap进行一个简要说明. 创建表和数据 创建教师表,班级表,学生表, 假设一个老师只负责教一个班,那么老师和班级之间的关系是一对一的关系. 假设一个班级有多个学生,那么班级和学生之间的关系是一对多的关系. CREATE TABLE teacher( t_id number(5) PRIMARY KEY, t_name VARCHAR2(20) ); CREATE TABLE class( c_id number(5)

mybatis 联表查询

一.一对一关联 1.1.提出需求 根据班级id查询班级信息(带老师的信息) 1.2.创建表和数据 创建一张教师表和班级表,这里我们假设一个老师只负责教一个班,那么老师和班级之间的关系就是一种一对一的关系. 1 CREATE TABLE teacher( 2 t_id INT PRIMARY KEY AUTO_INCREMENT, 3 t_name VARCHAR(20) 4 ); 5 CREATE TABLE class( 6 c_id INT PRIMARY KEY AUTO_INCREMEN

mybatis 关联表查询

这段时间由于项目上的需求:需要将数据库中两表关联的数据查询出来展示到前端(包含一对一,一对多): (1)一对一: 在实体类中维护了另一个类的对象: 这里我以用户(User)和产品(Product)为例:其中get和set的方法我就没有生成了,请自行生成: 实体类: public class User { private String id; private String name; private String password; private Product product; } publi

MyBatis 多表查询

1.多表设计 多表查询 a.多表设计 (1)一对一 在任意一方设计外键保存另一张表的主键,维系表和表的关系 (2)一对多 在多的一方设计外键保存一的一方的主键,维系表和表的关系 (3)多对多 设计一张第三方关系表,存储两张表的主键的对应关系,将一个多对多拆成两个一对多来存储 b.多表查询 笛卡尔积查询 内连接查询 外连接查询 左外连接查询 右外连接查询 全外连接查询 2.MyBatis中的一对一查询 create table room(id int primary key,name varcha

Mybatis多表查询之一对一查询的多种实现-XML配置

Mybatis 中对于多表查询提供了非常强大的实现方式,主要是通过resultMap的结果映射对于多表查询后的返回值进行封装,让我们来看一下官网上对于resultMap的解释:resultMap 元素是 MyBatis 中最重要最强大的元素.它可以让你从 90% 的 JDBC ResultSets 数据提取代码中解放出来,并在一些情形下允许你进行一些 JDBC 不支持的操作.实际上,在为一些比如连接的复杂语句编写映射代码的时候,一份 resultMap 能够代替实现同等功能的长达数千行的代码.R

04 数据库入门学习-单表查询、多表查询、子查询

1.复制表 #创建了一张测试表 mysql>create table test (id int primary key auto_increment,name char(10)); #显示原表结构 mysql> desc test; +-------+----------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+----------+---

关于oracle中系统自带的emp、dept表查询不带的问题

1.首先emp和dept表是scott用户下的,所以查询时要在scott用户下或者使用scott.emp: 2.如果emp表出来的不是系统自带的内容,而是自己添加的 一些东西,那么emp表已经别自己删掉了: 3.如果查询emp表显示不存在或者只有字段没有数据,那么emp表被自己无意中删掉了或者内容被自己删掉了. 恢复emp表或者dept表的方法: 将\\oracle\app\oracle\product\11.2.0\server\rdbms\admin\(地址前面一段可能有变化,看自己orac

mybatis多表查询,自动生成id

主要是在配置文件中,配置好所要包含的字段. 类关系:account----role,1对1 account包含role类 java类: public class Account{ private Integer accountId; private String username; private String password; private Role role; } public class Role { private Integer id; private Integer leave;

mybatis多表查询

1.在类的属性里定义另一个关联类,在mapper.xml里写result-mapper加association 2.表很多的话,可以在数据库里建视图view,把不同表里需要的字段写到一张视图里 写pojo类的时候直接根据视图来写,这样操作起来方便 3.把要用的字段写一个封装类,在select里多写几个语句,把查出来的结果再封装到这个类里 暂时只有这三个思路,有更好的办法再补充吧...