高级查询
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 标记来配合实现;至于多对多,也就是两个一对多而已。
标签:框架,int,查询,--,game,id,tb,public,搭建 From: https://www.cnblogs.com/BingBing-8888/p/18589232