mybatis——联合查询


  • 1.手动处理映射关系ResultMap

  • 2.数据准备

  • 3.一对一(one to one)关联查询

  • 4.一对多(one to many)关联查询

  • 5.多对多(many to many)关联查询


前面已经使用MyBatis完成了对Emp表的CRUD操作,不管是使用SqlSession直接操作,还是使用Mapper代理方式,都只是完成了对单个数据库表的操作。这肯定是远远不够的。在实际开发中,经常会将来自多张表的数据在一个位置显示。

Mybatis实现多表查询之关联查询

准备一个新的maven模块,练习mybatis多表查询。

注意:模块名最好全部小写,以避免无法解释的错误。

项目结构:

mybatis——联合查询

参考mybatis官方中文文档:https://mybatis.org/mybatis-3/zh/index.html

1.手动处理映射关系ResultMap

回顾:什么是映射关系?说白了就是实体类和数据库表字段之间封装时的关系。

当实体类和数据库表的字段名不一致的时候:

mybatis——联合查询

此时,如果不手动处理映射关系,会导致查询ename字段失败:

mybatis——联合查询
image-20221016121029333

上图中,实体类的其中一个属性是name,但是数据库表emp中对应 的字段名是ename,这时候就出现了不一致。

为了解决在查询时实体类封装的问题,需要手动处理映射关系。所使用的技术就是Mapper映射文件中引入ResultMap.

Mapper映射文件中需要引入ResultMap标签,来处理映射关系:

<!-- 处理映射关系 -->
<resultMap id="empMap" type="emp">
<!-- 如果是主键,建议使用id标签 -->
<id column="empno" property="empno"/>
<!-- 如果是普通字段,建议使用result标签 -->
<result column="ename" property="name"/>
<result column="mgr" property="mgr"/>
<result column="job" property="job"/>
<result column="hiredate" property="hiredate"/>
<result column="sal" property="sal"/>
<result column="comm" property="comm"/>
<result column="deptno" property="deptno"/>
</resultMap>

select标签查询时,在标签中引入相应id的ResultMap标签即可。

<!--List<Emp> findAll();-->
<select id="findAll" resultMap="empMap">
<include refid="empBaseSelect"/>
</select>

其中查询语句我用sql标签做了封装:

<!-- sql标签 -->
<sql id="empColumn">empno,ename,job,mgr,hiredate,sal,comm,deptno</sql>
<sql id="empBaseSelect">
select <include refid="empColumn"/> from emp
</sql>

这里补充一下,在数据库做ORM映射的时候,建议尽量不要使用*,这一点阿里巴巴开发手册中明确指出:

在表查询中,一律不要使用 * 作为查询的字段列表,需要哪些字段必须明确写明。

原因是:

说明:

1)增加查询分析器解析成本。

2)增减字段容易与 resultMap 配置不一致。

3)无用字段增加网络消耗,尤其是 text 类型的字段。

如何理解?

1、如果采用select * 进行查找时,查询到的列是按照它们在表的原始位置展示的;如果客户端同样采用列的原始位置进行引用,如果更改表结构,会导致难以察觉的错误;

2、使用时,数据库会先查数据字典,明确代表什么,这会在分析阶段造成大量开销;

3、select * 最大的问题是可能会多出一些不用的列,导致无法使用索引覆盖,导致查询成本几何层级的增加

4、不需要的字段会增加数据传输的时间,如果是本地客户端,连接的是本地的 mysql 服务器,tcp 协议传输数据会增加额外时间;如果是 db 和客户端不在同一台机器,比如连接到阿里云,则开销会更加明显

5、如果查询的时候获取了不必要的列,字段较多时,mysql 并非一次性保存,而是主次分布内存,当时用完后,再次分配。如此会导致多次分配,频繁分配会增加额外消耗时间

6、如果 sql 语句复杂,select * 会解析更多的对象,字段,权限,属性等内容,增加数据库负担

1.注意ResultMap标签的语法格式:

<resultMap id="empMap" type="emp">
<!-- 如果是主键,建议使用id标签 -->
<id column="empno" property="empno"/>
<!-- 如果是普通字段,建议使用result标签 -->
<result column="ename" property="name"/>
</resultMap>

其中有以下几个属性:

  • column:数据库中的列名,或者是列的别名。一般情况下,这和传递给                resultSet.getString(columnName) 方法的参数一样。
  • property:映射到列结果的字段或属性。如果 JavaBean有这个名字的属性(property),会先使用该属性。否则 MyBatis 将会寻找给定名称的字段(field)。(说白了就是实体类的属性名

除此之外还有几个属性值得关注:

  • javaType:java的数据类型,就是实体类封装的时候,指定的实体类的属性的类型。
  • jdbcType:JDBC 类型,其实就是数据库中某个字段 的类型  。

上面两个指定类型的属性,可以选用,我一般不使用,因为mybatis可以选用。

2.在select标签中ResultMap属性是对外部resultMap的命名引用。结果映射是 MyBatis 最强大的特性,如果对其理解透彻,许多复杂的映射问题都能迎刃而解。**resultType resultMap 之间只能同时使用一个。**

3.ResultMap可以让开发者从 90% 的 JDBC ResultSets 数据提取代码中解放出来,并在一些情形下允许开发者进行一些JDBC 不支持的操作。实际上,在为一些比如连接的复杂语句编写映射代码的时候,一份resultMap 能够代替实现同等功能的数千行代码。ResultMap的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。

4.上面的代码:

<resultMap id="empMap" type="emp">
<!-- 如果是主键,建议使用id标签 -->
<id column="empno" property="empno"/>
<!-- 如果是普通字段,建议使用result标签 -->
<result column="ename" property="name"/>
</resultMap>

是显式的指定了resultMap.其实也可以隐式地制定ResultMap,但是不推荐,第一就是复用性不高,也会造成select等sql语句很臃肿。

5.不得不赞叹mybatis对于结果映射的设计思想:

数据库不可能永远是你所想或所需的那个样子。我们希望每个数据库都具备良好的第三范式或 BCNF 范式,可惜它们并不都是那样。如果能有一种数据库映射模式,完美适配所有的应用程序,那就太好了,但可惜也没有。 而 ResultMap 就是 MyBatis 对这个问题的答案。

2.数据准备

在正式进入到mybatis多表查询的学习之前,需要准备以下数据,sql代码提供如下:

(在数据库mydb下新建两个表projectsprojectrecord

CREATE TABLE `projects`  (
`pid` int(2) NOT NULL AUTO_INCREMENT,
`pname` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
`money` int(11) NULL DEFAULT NULL,
PRIMARY KEY (`pid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

INSERT INTO `projects` VALUES (1, ' ***大学OA', 500000);
INSERT INTO `projects` VALUES (2, '学生选课系统', 100000);
INSERT INTO `projects` VALUES (3, '讲师测评系统', 20000);
INSERT INTO `projects` VALUES (4, '线上问答系统 ', 20000);

CREATE TABLE `projectrecord` (
`empno` int(4) NOT NULL,
`pid` int(2) NOT NULL,
PRIMARY KEY (`empno`, `pid`) USING BTREE,
INDEX `fk_project_pro`(`pid`) USING BTREE,
CONSTRAINT `fk_emp_pro` FOREIGN KEY (`empno`) REFERENCES `emp` (`EMPNO`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `fk_project_pro` FOREIGN KEY (`pid`) REFERENCES `projects` (`pid`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;


INSERT INTO `projectrecord` VALUES (7369, 1);
INSERT INTO `projectrecord` VALUES (7521, 1);
INSERT INTO `projectrecord` VALUES (7369, 2);
INSERT INTO `projectrecord` VALUES (7499, 2);
INSERT INTO `projectrecord` VALUES (7521, 2);
INSERT INTO `projectrecord` VALUES (7369, 3);
INSERT INTO `projectrecord` VALUES (7499, 3);
INSERT INTO `projectrecord` VALUES (7521, 3);
INSERT INTO `projectrecord` VALUES (7369, 4);
INSERT INTO `projectrecord` VALUES (7499, 4);

如果你还没有mydb这个数据库,sql代码也提供如下:

/*
Navicat Premium Data Transfer

Source Server : localhost_3306
Source Server Type : MySQL
Source Server Version : 80018
Source Host : localhost:3306
Source Schema : mydb

Target Server Type : MySQL
Target Server Version : 80018
File Encoding : 65001

Date: 24/12/2020 13:34:06
*/


SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for bonus
-- ----------------------------
DROP TABLE IF EXISTS `bonus`;
CREATE TABLE `bonus` (
`ENAME` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`JOB` varchar(9) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`SAL` double(7, 2) NULL DEFAULT NULL,
`COMM` double(7, 2) NULL DEFAULT NULL
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of bonus
-- ----------------------------

-- ----------------------------
-- Table structure for dept
-- ----------------------------
DROP TABLE IF EXISTS `dept`;
CREATE TABLE `dept` (
`DEPTNO` int(2) NOT NULL,
`DNAME` varchar(14) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`LOC` varchar(13) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
PRIMARY KEY (`DEPTNO`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of dept
-- ----------------------------
INSERT INTO `dept` VALUES (10, 'ACCOUNTING', 'NEW YORK');
INSERT INTO `dept` VALUES (20, 'RESEARCH', 'DALLAS');
INSERT INTO `dept` VALUES (30, 'SALES', 'CHICAGO');
INSERT INTO `dept` VALUES (40, 'OPERATIONS', 'BOSTON');

-- ----------------------------
-- Table structure for emp
-- ----------------------------
DROP TABLE IF EXISTS `emp`;
CREATE TABLE `emp` (
`EMPNO` int(4) NOT NULL,
`ENAME` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`JOB` varchar(9) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`MGR` int(4) NULL DEFAULT NULL,
`HIREDATE` date NULL DEFAULT NULL,
`SAL` double(7, 2) NULL DEFAULT NULL,
`COMM` double(7, 2) NULL DEFAULT NULL,
`DEPTNO` int(2) NULL DEFAULT NULL,
PRIMARY KEY (`EMPNO`) USING BTREE,
INDEX `FK_DEPTNO`(`DEPTNO`) USING BTREE,
CONSTRAINT `FK_DEPTNO` FOREIGN KEY (`DEPTNO`) REFERENCES `dept` (`DEPTNO`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of emp
-- ----------------------------
INSERT INTO `emp` VALUES (7369, 'SMITH', 'CLERK', 7902, '1980-12-17', 800.00, NULL, 20);
INSERT INTO `emp` VALUES (7499, 'ALLEN', 'SALESMAN', 7698, '1981-02-20', 1600.00, 300.00, 30);
INSERT INTO `emp` VALUES (7521, 'WARD', 'SALESMAN', 7698, '1981-02-22', 1250.00, 500.00, 30);
INSERT INTO `emp` VALUES (7566, 'JONES', 'MANAGER', 7839, '1981-04-02', 2975.00, NULL, 20);
INSERT INTO `emp` VALUES (7654, 'MARTIN', 'SALESMAN', 7698, '1981-09-28', 1250.00, 1400.00, 30);
INSERT INTO `emp` VALUES (7698, 'BLAKE', 'MANAGER', 7839, '1981-05-01', 2850.00, NULL, 30);
INSERT INTO `emp` VALUES (7782, 'CLARK', 'MANAGER', 7839, '1981-06-09', 2450.00, NULL, 10);
INSERT INTO `emp` VALUES (7788, 'SCOTT', 'ANALYST', 7566, '1987-04-19', 3000.00, NULL, 20);
INSERT INTO `emp` VALUES (7839, 'KING', 'PRESIDENT', NULL, '1981-11-17', 5000.00, NULL, 10);
INSERT INTO `emp` VALUES (7844, 'TURNER', 'SALESMAN', 7698, '1981-09-08', 1500.00, 0.00, 30);
INSERT INTO `emp` VALUES (7876, 'ADAMS', 'CLERK', 7788, '1987-05-23', 1100.00, NULL, 20);
INSERT INTO `emp` VALUES (7900, 'JAMES', 'CLERK', 7698, '1981-12-03', 950.00, NULL, 30);
INSERT INTO `emp` VALUES (7902, 'FORD', 'ANALYST', 7566, '1981-12-03', 3000.00, NULL, 20);
INSERT INTO `emp` VALUES (7934, 'MILLER', 'CLERK', 7782, '1982-01-23', 1300.00, NULL, 10);

-- ----------------------------
-- Table structure for salgrade
-- ----------------------------
DROP TABLE IF EXISTS `salgrade`;
CREATE TABLE `salgrade` (
`GRADE` int(11) NOT NULL,
`LOSAL` double(7, 2) NULL DEFAULT NULL,
`HISAL` double(7, 2) NULL DEFAULT NULL,
PRIMARY KEY (`GRADE`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of salgrade
-- ----------------------------
INSERT INTO `salgrade` VALUES (1, 700.00, 1200.00);
INSERT INTO `salgrade` VALUES (2, 1201.00, 1400.00);
INSERT INTO `salgrade` VALUES (3, 1401.00, 2000.00);
INSERT INTO `salgrade` VALUES (4, 2001.00, 3000.00);
INSERT INTO `salgrade` VALUES (5, 3001.00, 9999.00);

SET FOREIGN_KEY_CHECKS = 1;

完成数据库的准备工作之后,数据库是这样的:

mybatis——联合查询

project表如下:

mybatis——联合查询

每一个project都会有几个emp去完成,这个关系体现在projectrecord表中:

mybatis——联合查询

为了更加形象的体现几个数据库表之间的关系,可以通过navicat的逆向数据库模型Reverse database to model来查看:

mybatis——联合查询

mybatis——联合查询
image-20221016125736436

3.一对一(one to one)关联查询

来看一个需求:根据编号查询员工信息及所在的部门信息

如果是在navicat中进行查询:

select * from emp e left join dept d on e.DEPTNO = d.DEPTNO where empno = 7566;
mybatis——联合查询
image-20221016134600172

对于多表查询,在实体类的设计上会有区别。

【1】实体类添加一个部门作为属性

package com.bones.pojo;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;
import java.util.Date;

/**
* @author : bones
* @version : 1.0
*/

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Emp implements Serializable {
private Integer empno;
private String name;
private String job;
private Integer mgr;
private Date hiredate;
private Double sal;
private Double comm;
private Integer deptno;
//多表查询:组合Dept作为属性
private Dept dept;
}

【2】mapper接口的抽象方法:

/**
* 根据员工编号查询员工信息及员工所在部门的信息
* @param empno 员工编号
* @return Emp对象,封装了员工信息及员工所在部门的信息
*/

Emp findEmpJoinDeptByEmpno(int empno);

【3】Mapper映射文件的查询:

结果映射中专门用于处理一对一的association

<resultMap id="empJoinDept" type="emp">
<!-- 如果是主键,建议使用id标签 -->
<id column="empno" property="empno"/>
<!-- 如果是普通字段,建议使用result标签 -->
<result column="ename" property="name"/>
<result column="mgr" property="mgr"/>
<result column="job" property="job"/>
<result column="hiredate" property="hiredate"/>
<result column="sal" property="sal"/>
<result column="comm" property="comm"/>
<result column="deptno" property="deptno"/>
<!-- association专门用于一对一查询处理
property emp类的属性名
javaType 用哪个类的对象给属性赋值(下面写的是别名)
-->

<association property="dept" javaType="dept">
<id column="deptno" property="deptno"/>
<result property="dname" column="dname"/>
<result property="loc" column="loc"/>
</association>
</resultMap>

查询标签:

<!--Emp findEmpJoinDeptByEmpno(int empno);-->
<select id="findEmpJoinDeptByEmpno" resultMap="empJoinDept">
select * from emp e
left join dept d
on e.DEPTNO = d.DEPTNO
where empno = #{empno};
</select>

测试方法:

@Test
public void testFindEmpJoinDeptByEmpno(){
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
Emp emp = mapper.findEmpJoinDeptByEmpno(7566);
System.out.println(emp);
}

4.一对多(one to many)关联查询

需求:根据部门编号deptno,查询出部门的信息,以及在这个部门的员工的信息。

显然这时候,一个部门dept对应多个员工emp,这就是一对多的查询要求。

尝试在navicat中书写sql语句:(这是一个好习惯,助于排错。因为在mybatis运行时报错进行排错,可能将是一件非常费力的事情,毕竟是框架)

select * from dept d left join emp e on d.deptno = e.deptno where d.deptno = 20;

查询结果是:

mybatis——联合查询
image-20221016143345493

左边的数据实际就是一条deptno为20的数据,而右边就是一对多的“多”。

这里补充一个mysql的小知识,在一开始书写sql语句的时候,我写成了:

select * from dept d left join emp e on d.deptno = e.deptno where deptno = 20;

控制台报错为:

> 1052 - Column 'deptno' in where clause is ambiguous

意思是字段deptno变得模糊不清,因为这个字段在emp表和dept表中都出现了。所以,建议将查询条件改为d.deptno = 20更加好。这应该是书写mysql语句的时候比较容易出现的错误了吧。

下面开始在mybatis中完善这个一对多的查询功能。

【1】Dept表格的字段:(为了实现一对多,必须将对应的多个Emp对象封装为一个集合,作为Dept表的一个属性来存储)

package com.bones.pojo;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;
import java.util.List;

/**
* @author : bones
* @version : 1.0
*/

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Dept implements Serializable {
private Integer deptno;
private String dname;
private String loc;
//组合Emp的List集合作为属性(一对多one to many)
private List<Emp> empList;
}

【2】Mapper接口定义查询的抽象方法:

package com.bones.mapper;

import com.bones.pojo.Dept;

/**
* @author : bones
* @version : 1.0
*/

public interface DeptMapper {
/**
* 根据部门编号查询部门的相关信息,并且返回所在部门所有员工的信息
* @param deptno 要查询的部门编号
* @return 返回部门dept对象,对象中包含有封装多个Emp对象的List集合的属性
*/

Dept findDeptJoinEmpByDeptno(int deptno);
}

【3】准备DeptMapper.xml映射文件(千万别忘记将DeptMapper映射文件注册在sqlMapConfig.xmlmappers标签中)

一定要注意:collection里面是ofType属性,不是javaType属性

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.bones.mapper.DeptMapper">

<sql id="deptColumn">deptno,dname,loc</sql>
<sql id="deptAllSelect">
select <include refid="deptColumn"/> from dept
</sql>


<!-- 一对多映射用collection标签-->
<resultMap id="deptJoinEmpByDeptno" type="dept">
<!-- 首先处理dept表的3个字段的映射关系-->
<id column="deptno" property="deptno"/>
<result column="dname" property="dname"/>
<result column="loc" property="loc"/>

<!--
property 写的是dept实体类中对应的属性名
javaType 写的是对应的用哪个实体类进行封装
-->

<collection property="empList" ofType="emp">
<id column="empno" property="empno"/>
<result column="ename" property="ename"/>
<result column="job" property="job"/>
<result column="mgr" property="mgr"/>
<result column="hiredate" property="hiredate"/>
<result column="sal" property="sal"/>
<result column="comm" property="comm"/>
<result column="deptno" property="deptno"/>
</collection>
</resultMap>

<!--Dept findDeptJoinEmpByDeptno(int deptno);-->
<select id="findDeptJoinEmpByDeptno" resultMap="deptJoinEmpByDeptno">
<!-- <include refid="deptAllSelect"/> as d-->
<!-- left join emp e on d.deptno = e.deptno-->
<!-- where d.deptno = #{deptno}-->

select * from dept d left join emp e on d.deptno = e.deptno where d.deptno = #{deptno}
</select>

</mapper>

测试方法:

@Test
public void testFindDeptJoinEmpBydeptno(){
DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
Dept dept = mapper.findDeptJoinEmpByDeptno(20);
List<Emp> emps = dept.getEmpList();
emps.forEach(System.out::println);
}

以上mapper映射文件中如果要规避select *的问题,就可以这么写:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.bones.mapper.DeptMapper">

<sql id="deptColumnAlias">d.deptno,d.dname,d.loc</sql>

<sql id="empColumnAlias">e.empno,e.ename,e.job,e.mgr,e.hiredate,e.sal,e.comm,e.deptno</sql>



<!-- 一对多映射用collection标签-->
<resultMap id="deptJoinEmpByDeptno" type="dept">
<!-- 首先处理dept表的3个字段的映射关系-->
<id column="deptno" property="deptno"/>
<result column="dname" property="dname"/>
<result column="loc" property="loc"/>

<!--
property 写的是dept实体类中对应的属性名
javaType 写的是对应的用哪个实体类进行封装
-->

<collection property="empList" ofType="emp">
<id column="empno" property="empno"/>
<result column="ename" property="ename"/>
<result column="job" property="job"/>
<result column="mgr" property="mgr"/>
<result column="hiredate" property="hiredate"/>
<result column="sal" property="sal"/>
<result column="comm" property="comm"/>
<result column="deptno" property="deptno"/>
</collection>
</resultMap>

<!--Dept findDeptJoinEmpByDeptno(int deptno);-->
<select id="findDeptJoinEmpByDeptno" resultMap="deptJoinEmpByDeptno">
select <include refid="deptColumnAlias"/>,<include refid="empColumnAlias"/>
from dept d left join emp e
on d.deptno = e.deptno
where d.deptno = #{deptno}

<!-- select * from dept d left join emp e on d.deptno = e.deptno where d.deptno = #{deptno}-->
</select>

</mapper>

5.多对多(many to many)关联查询

需求:根据项目编号查询项目信息,以及参与到该项目之中的所有的员工信息

在navicat中书写sql语句:

select p.pid,p.pname,p.money,e.empno,e.ENAME,e.JOB,e.MGR,e.HIREDATE,e.SAL,e.COMM,e.DEPTNO
from project p
left join projectrecord pr
on pr.pid = p.pid
left join emp e
on pr.empno = e.EMPNO
where p.pid = 3;

查询结果:

mybatis——联合查询
image-20221016194415740

这里为了符合阿里巴巴的java开发手册中的规定“在表查询中,一律不要使用 * 作为查询的字段列表,需要哪些字段必须明确写明” ,我将要查找的内容将*变成了具体的字段。

【1】准备实体类ProjectProjectRecord:

实体类Project.java

package com.bones.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;
import java.util.List;

/**
* @author : bones
* @version : 1.0
*/

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Project implements Serializable {
private Integer pid;
private String pname;
private Integer money;
private List<ProjectRecord> projectRecordList;
}

实体类ProjectRecord.java

package com.bones.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

/**
* @author : bones
* @version : 1.0
*/

@Data
@AllArgsConstructor
@NoArgsConstructor
public class ProjectRecord implements Serializable {
private Integer pid;
private Integer empno;
private Emp emp;
}

【2】Mapper接口:

ProjectMapper.java

package com.bones.mapper;

import com.bones.pojo.Project;

/**
* @author : bones
* @version : 1.0
*/

public interface ProjectMapper {
/**
* 根据项目编号查询项目的信息和参与项目的所有员工的信息
* @param pid 项目编号
* @return Project对象,包含参与项目的员工封装的List集合
*/

Project findProjectJoinEmpBypid(int pid);
}

【3】映射文件:

ProjectMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.bones.mapper.ProjectMapper">

<sql id="empColumn">
e.empno,e.ENAME,e.JOB,e.MGR,e.HIREDATE,e.SAL,e.COMM,e.DEPTNO
</sql>
<sql id="projectColumn">
p.pid,p.pname,p.money
</sql>

<resultMap id="ProjectJoinEmp" type="project">
<id property="pid" column="pid"/>
<result property="pname" column="pname"/>
<result property="money" column="money"/>
<!-- 一对多就用collection -->
<collection property="projectRecordList" ofType="projectRecord">
<id column="pid" property="pid"/>
<id column="empno" property="empno"/>
<association property="emp" javaType="emp">
<!-- 如果是主键,建议使用id标签 -->
<id column="empno" property="empno"/>
<!-- 如果是普通字段,建议使用result标签 -->
<result column="ename" property="ename"/>
<result column="mgr" property="mgr"/>
<result column="job" property="job"/>
<result column="hiredate" property="hiredate"/>
<result column="sal" property="sal"/>
<result column="comm" property="comm"/>
<result column="deptno" property="deptno"/>
</association>
</collection>

</resultMap>
<!-- Project findProjectJoinEmpBypid(int pid);-->
<select id="findProjectJoinEmpBypid" resultMap="ProjectJoinEmp">
select <include refid="projectColumn"/>,<include refid="empColumn"/>
from project p
left join projectrecord pr
on pr.pid = p.pid
left join emp e
on pr.empno = e.EMPNO
where p.pid = #{pid}
</select>
</mapper>

【4】测试方法:

@Test
public void testFindProjectJoinEmp(){
ProjectMapper mapper = sqlSession.getMapper(ProjectMapper.class);
Project project = mapper.findProjectJoinEmpBypid(3);
System.out.println(project.getPid());
System.out.println(project.getPname());
System.out.println(project.getMoney());
System.out.println("--打印所有参与的员工----");
List<ProjectRecord> projectRecordList = project.getProjectRecordList();
for (ProjectRecord projectRecord : projectRecordList) {
System.out.println(projectRecord.getEmp());
}
}

多对多查询总结

其实多对多是相对于数据库表来说的,其实一本都会将多对多转化成一对多,就像上面的写法,在一个实体类中加入属性(另一个实体类的List)。

关键是理清是转化成了一对多还是一对一,如果是一对多就用Collection标签,如果是一对一就用association标签。

mybatis——联合查询


原文始发于微信公众号(小东方不败):mybatis——联合查询

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/47415.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!