技术框架中对高级查询环境搭建学习

高级查询

MyBatis 高级查询

之前在学习 Mapper XML 映射文件时,说到 resultMap 标记是 MyBatis 中最重要最强大也是最复杂的标记,而且还提到后面会详细介绍它的高级用法。

听到高级用法不要觉得有多高级,说白了就是联表查询

MyBatis 支持三种联表查询方式:

  • 一对一关联查询
  • 一对多关联查询
  • 多对多关联查询

联表查询需要用到 resultMap 标记两个子标记:

  • association 标记:用于映射关联查询单个对象的信息
  • collection 标记:用于映射关联查询多个对象的信息

环境准备

创建数据库表

为了演示高级查询,我准备了四张表,想必大家都喜欢玩游戏,那么这四张表和游戏有关,分别是:

  • 玩家表(tb_player)
  • 游戏表(tb_game)
  • 账号表(tb_account)
  • 角色表(tb_role)

简单分析一下这四张表的关系如下:

  • 玩家表->游戏表:多对多(一个玩家有多个游戏,一个游戏有多个玩家)
  • 游戏表->账号表:一对多(一个游戏有多个账号,一个账号有一个游戏)
  • 账号表->角色表:一对一(一个账号有一个角色,一个角色有一个账号)

编写 SQL 语句分别创建四张表,如下:

复制代码
SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for `tb_account`
-- ----------------------------
DROP TABLE IF EXISTS `tb_account`;
CREATE TABLE `tb_account` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_name` varchar(255) DEFAULT NULL,
  `password` varchar(255) DEFAULT NULL,
  `game_id` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `game_id` (`game_id`) USING BTREE,
  CONSTRAINT `tb_account_ibfk_1` FOREIGN KEY (`game_id`) REFERENCES `tb_game` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of tb_account
-- ----------------------------
INSERT INTO `tb_account` VALUES ('1', '潇洒哥', '12345', '1');
INSERT INTO `tb_account` VALUES ('4', '进击巨人', '11111', '1');
INSERT INTO `tb_account` VALUES ('5', '征服者', '12315', '2');

-- ----------------------------
-- Table structure for `tb_game`
-- ----------------------------
DROP TABLE IF EXISTS `tb_game`;
CREATE TABLE `tb_game` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `type` varchar(255) DEFAULT NULL,
  `operator` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of tb_game
-- ----------------------------
INSERT INTO `tb_game` VALUES ('1', '英雄联盟', 'MOBA', '腾讯游戏');
INSERT INTO `tb_game` VALUES ('2', '绝地求生', 'TPS', '蓝洞游戏');

-- ----------------------------
-- Table structure for `tb_player`
-- ----------------------------
DROP TABLE IF EXISTS `tb_player`;
CREATE TABLE `tb_player` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `sex` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of tb_player
-- ----------------------------
INSERT INTO `tb_player` VALUES ('1', '张三', '23', '1');
INSERT INTO `tb_player` VALUES ('2', '李四', '24', '1');

-- ----------------------------
-- Table structure for `tb_player_game`
-- ----------------------------
DROP TABLE IF EXISTS `tb_player_game`;
CREATE TABLE `tb_player_game` (
  `player_id` int(11) NOT NULL DEFAULT '0',
  `game_id` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`player_id`,`game_id`),
  KEY `game_id` (`game_id`),
  CONSTRAINT `tb_player_game_ibfk_2` FOREIGN KEY (`game_id`) REFERENCES `tb_game` (`id`),
  CONSTRAINT `tb_player_game_ibfk_1` FOREIGN KEY (`player_id`) REFERENCES `tb_player` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of tb_player_game
-- ----------------------------
INSERT INTO `tb_player_game` VALUES ('1', '1');
INSERT INTO `tb_player_game` VALUES ('1', '2');
INSERT INTO `tb_player_game` VALUES ('2', '2');

-- ----------------------------
-- Table structure for `tb_role`
-- ----------------------------
DROP TABLE IF EXISTS `tb_role`;
CREATE TABLE `tb_role` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `profession` varchar(255) DEFAULT NULL,
  `rank` int(11) DEFAULT NULL,
  `money` int(11) DEFAULT NULL,
  `account_id` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `account_id` (`account_id`) USING BTREE,
  CONSTRAINT `tb_role_ibfk_1` FOREIGN KEY (`account_id`) REFERENCES `tb_account` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of tb_role
-- ----------------------------
INSERT INTO `tb_role` VALUES ('1', '战士', '10', '2000', '1');
INSERT INTO `tb_role` VALUES ('2', '法师', '30', '100000', '4');
INSERT INTO `tb_role` VALUES ('4', '刺客', '1', '100', '5');

数据库显示如下:

这张图是通过 Navicat 工具的逆向表模型功能得到的,可以直观方便查看表与表之间的关系,操作步骤如下:

你可能会问,不是说四张表,怎么数据库里有五张表呀,原因是多对多的关联关系在数据库中无法直接实现,只能间接实现;具体而言,就是通过添加一张中间表,将多对多的关联关系转化为两个一对多的关联关系。

如果还没搞明白,建议你去复习一下数据库的相关知识。

创建 Java 实体类

我们需要创建数据库四张表对应的 Java 实体类,在之前的 Maven 工程的 main/java/entity/ 下分别创建 PlayerEntity.java、GameEntity.java、AccountEntity.java、RoleEntity.java 四个文件,如下:

复制代码
package entity;

/**
 * @desc 游戏玩家实体类
 * @date 2020/7/10 下午6:01
 */
public class PlayerEntity {
    private int id;
    private String name;
    private int age;
    private int sex;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getSex() {
        return sex;
    }

    public void setSex(int sex) {
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "PlayerEntity{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", sex=" + sex +
                '}';
    }
}
复制代码
package entity;

/**
 * @desc 游戏实体类
 * @date 2020/7/10 下午6:02
 */
public class GameEntity {
    private int id;
    private String name;
    private String type;
    private String operator;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getOperator() {
        return operator;
    }

    public void setOperator(String operator) {
        this.operator = operator;
    }

    @Override
    public String toString() {
        return "GameEntity{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", type='" + type + '\'' +
                ", operator='" + operator + '\'' +
                '}';
    }
}
复制代码
package entity;

/**
 * @desc 游戏账号实体类
 * @date 2020/7/10 下午6:04
 */
public class AccountEntity {
    private int id;
    private String userName;
    private String password;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "AccountEntity{" +
                "id=" + id +
                ", userName='" + userName + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

复制代码
package entity;

/**
 * @desc 游戏角色实体类
 * @date 2020/7/10 下午6:08
 */
public class RoleEntity {
    private int id;
    private String profession;
    private int rank;
    private int money;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getProfession() {
        return profession;
    }

    public void setProfession(String profession) {
        this.profession = profession;
    }

    public int getRank() {
        return rank;
    }

    public void setRank(int rank) {
        this.rank = rank;
    }

    public int getMoney() {
        return money;
    }

    public void setMoney(int money) {
        this.money = money;
    }

    @Override
    public String toString() {
        return "RoleEntity{" +
                "id=" + id +
                ", profession='" + profession + '\'' +
                ", rank=" + rank +
                ", money=" + money +
                '}';
    }
}
创建映射器接口

在之前的 Maven 工程的 main/java/mapper/ 下创建 GameMapper.java 接口文件,如下:

复制代码
package mapper;

import entity.RoleEntity;

/**
 * @desc 游戏映射器接口
 * @date 2020/7/10 下午6:20
 */
public interface GameMapper {
    
}

目前接口类中没有方法,后续添加查询需求时再添加对应的接口方法。

你可能会有点疑惑,之前创建了四个 Java 实体类对应四张表,为何这里只创建了一个 Java 映射器接口?是不是也应该创建四个?

MyBatis 没有必须要求一个 Java 实体类一定要对应一个 Java 映射器接口。创建多少个映射器接口,一般根据项目的可阅读性和可维护性决定的。

由于我们的查询需求比较简单,不存在可阅读性和可维护性问题,所以创建一个映射器接口就足够了。

创建 XML 映射文件

在之前的 Maven 工程的 main/resources/mappers 下创建 GameMapper.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="mapper.GameMapper">
    
</mapper>

由于映射器接口没有方法,所以映射文件也是空的。

全局配置中添加 XML 映射文件路径

在 Maven 工程中打开 main/resources/mybatis-config.xml 全局配置文件,添加之前创建的 GameMapper.xml 文件路径,如下:

复制代码
		<!--配置映射文件路径-->
    <mappers>
        <mapper resource="mappers/UserMapper.xml" />
      	<!--添加 GameMapper.xml 映射文件路径-->
        <mapper resource="mappers/GameMapper.xml" />
    </mappers>

至此,基本的环境已经准备好了。

接下来,我们还需要复习一下数据库的联表查询相关的知识。

所谓联表查询指的是从数据库相关联的多张表中查询信息,数据库中联表查询的方式有两种,如下:

  • 关联查询(关键字 JOIN)

    关联查询细分为四种类型,分别为左关联(LEFT JOIN)、右关联(RIGHT JOIN)、自关联(INNER JOIN)和全关联(FULL JOIN),而直接写 JOIN 默认是自关联。关于它们之间的区别,这里我就不赘述了,搞不懂的同学,还请去复习数据库。

  • 子查询 (关键字 SELECT)

    子查询也叫嵌套查询

关联查询和子查询二者各有优缺点,如下:

  • 关联查询优缺点:优点是性能好,查询过程不产生临时表;缺点是 SQL 语句复杂,可复用性低
  • 子查询优缺点:优点是 SQL 语句简单,可复用性高;缺点是性能差,查询过程要产生临时表

实际项目开发,为了追求性能,一般多用关联查询。

既然数据库联表查询有两种方式,那么 MyBatis 的联表查询也支持这两种方式;在 MyBatis 中一对一和一对多可是使用 association 或者 collection 标记来配合实现;至于多对多,也就是两个一对多而已。

posted @   BingBing爱化学-04044  阅读(5)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示