多对一、一对多查询笔记

一、项目概述

经典的部门(Dept)与员工(Emp)之间的多对一和一对多关系查询。

二、实体类设计

1. 部门实体(Dept)

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Dept {
    private Long deptNo;      // 部门编号
    private String dname;     // 部门名称
    private String loc;       // 部门位置

    // 一对多关系:一个部门包含多个员工
    private List<Emp> empList;
}

2. 员工实体(Emp)

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Emp {
    private Long empno;       // 员工编号
    private String ename;      // 员工姓名
    private String job;        // 职位
    private Long mgr;         // 上级编号
    private Date hiredate;    // 入职日期
    private Double sal;       // 薪资
    private Double comm;      // 奖金
    private Long deptno;      // 部门编号(外键)

    // 多对一关系:多个员工属于一个部门
    private Dept dept;
}

三、多对一查询(Emp → Dept)

1. 使用 <association> 标签

场景:查询员工信息时,同时查询该员工所属的部门信息。

实现方式:使用嵌套查询(N+1 查询问题)

<resultMap id="empMap" type="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"/>
    
    <!-- 多对一关联:通过 select 属性指定查询部门的方法 -->
    <association property="dept" javaType="cn.wolfcode.domain.Dept"
                 select="cn.wolfcode.mapper.DeptMapper.selectById" column="deptno">
    </association>
</resultMap>

<select id="findAll" resultMap="empMap">
    select empno,ename,job,mgr,hiredate,sal,comm,emp.deptno,dept.deptno as dno,dname,loc
    from emp left join dept on emp.deptno = dept.deptno
</select>

关键点说明

  • property:指定 Emp 类中的属性名(dept)
  • javaType:指定关联对象的类型
  • select:指定另一个 Mapper 中的查询方法(会触发额外的查询)
  • column:将当前查询的 deptno 列作为参数传递给 select 查询

四、一对多查询(Dept → Emp)

1. 使用 <collection> 标签

场景:查询部门信息时,同时查询该部门下的所有员工列表。

实现方式:使用左外连接 + 复用 resultMap

<resultMap id="deptMap" type="Dept">
    <id column="dno" property="deptNo"/>
    <result column="dname" property="dname"/>
    <result column="loc" property="loc"/>
    
    <!-- 一对多关联:使用 collection 标签 -->
    <collection property="empList" ofType="cn.wolfcode.domain.Emp"
                resultMap="cn.wolfcode.mapper.EmpMapper.empMap1">
    </collection>
</resultMap>

<!-- 定义可复用的员工映射 -->
<resultMap id="empMap1" type="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"/>
</resultMap>

<select id="findAll" resultMap="deptMap">
    select dept.deptno as dno, dname, loc, empno, ename, job, mgr, hiredate, sal, comm, emp.deptno
    from dept left join emp on dept.deptno = emp.deptno
</select>

关键点说明

  • property:指定 Dept 类中的属性名(empList)
  • ofType:指定集合中元素的类型(Emp)
  • resultMap:复用另一个 Mapper 中定义的 resultMap
  • 使用左外连接确保即使部门没有员工也能查询到部门信息

五、测试代码

1. 多对一查询测试

@Test
public void test() {
    // 查询全部员工信息(包含部门信息)
    List<Emp> empList = empMapper.findAll();
    empList.stream().forEach(System.out::println);
}

输出示例

Emp(empno=7369, ename=SMITH, job=CLERK, mgr=7902, hiredate=..., sal=800.0, comm=null, deptno=20, dept=Dept(deptNo=20, dname=RESEARCH, loc=DALLAS, empList=null))

2. 一对多查询测试

@Test
public void test2() {
    // 查询全部部门信息(包含员工列表)
    List<Dept> deptList = deptMapper.findAll();
    deptList.stream().forEach(System.out::println);
}

输出示例

Dept(deptNo=20, dname=RESEARCH, loc=DALLAS, empList=[Emp(...), Emp(...), ...])

六、核心知识点总结

1. <association> 标签(多对一)

  • 用途:处理"多"方关联"一"方的关系
  • 属性
    • property:Java 实体类中的属性名
    • javaType:关联对象的完整类名
    • select:指定嵌套查询的 statement ID
    • column:传递给嵌套查询的列名
  • 适用场景:查询员工时需要获取部门信息

2. <collection> 标签(一对多)

  • 用途:处理"一"方关联"多"方的关系
  • 属性
    • property:Java 实体类中的集合属性名
    • ofType:集合中元素的类型
    • resultMap:复用已定义的 resultMap
  • 适用场景:查询部门时需要获取员工列表

3. resultMap 复用

  • 通过 resultMap="命名空间.resultMapId" 可以跨 Mapper 复用映射配置
  • 本例中 DeptMapper.xml 复用了 EmpMapper.xml 中的 empMap1

4. SQL 片段复用

<sql id="deptColumns">
    dept.deptno as dno, dname, loc, empno, ename, job, mgr, hiredate, sal, comm, emp.deptno
</sql>

<select id="findAll" resultMap="deptMap">
    select <include refid="deptColumns"/>
    from dept left join emp on dept.deptno = emp.deptno
</select>
posted on 2026-01-28 19:48  关羽飞  阅读(0)  评论(0)    收藏  举报