超多干货!!!MyBatis多表关联查询处理结果集、懒加载、association(一对一)、collection(一对多)、案例详解

本文详细介绍了在MyBatis中如何处理多表关联查询,包括一对一和一对多结果映射,以及如何使用resultMap配置association和collection。同时,讲解了懒加载的原理和配置,通过案例展示了如何实现懒加载查询部门及其下属员工信息,以减少数据库压力。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

多表关联查询处理结果集

我们使用MyBatis对单张表进行输出映射是一个比较简单的过程,可以直接将结果封装到POJO(普通java对象)对象中,就算是java类的属性名和数据库的列名不完全一致,我们也可以使用resultMap进行配置,但是当我们多表关联查询时,这些手段就跟不上我们的步伐了,下面用一个例子(部门-员工)为大家详细演示多表关联查询。

准备阶段

1.建立数据库表(部门表t_dept和员工表t_emp)

-- 部门表
CREATE TABLE `t_dept` (
  `id` SMALLINT NOT NULL AUTO_INCREMENT COMMENT'部门id',
  `name` VARCHAR(10) DEFAULT NULL COMMENT'部门名称',
  `dept_desc` VARCHAR(100) DEFAULT NULL '部门口号' ,
  PRIMARY KEY (`id`)
)
-- 员工表 员工所属部门为部门表id
CREATE TABLE `t_emp` (
  `id` INT NOT NULL AUTO_INCREMENT COMMENT'员工id',
  `name` VARCHAR(10) DEFAULT NULL COMMENT'员工姓名',
  `adress` VARCHAR(10) DEFAULT NULL COMMENT'员工地址',
  `dept_id` SMALLINT DEFAULT NULL COMMENT'员工所属部门',
  PRIMARY KEY (`id`)
)

2.新建Java类(Dept.java和Emp.java)

package com.cwd.mybatis.bean;

/**
 * @Author Lotus_dong
 * @Date
 * @function 员工表 员工与部门为一对一关系
 */
public class Emp {
    private int id;
    private  String name;
    private String adress;
    private Dept dept;//员工所属部门对象 一个员工对应一个部门

    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 getAdress() {
        return adress;
    }

    public void setAdress(String adress) {
        this.adress = adress;
    }

    public Dept getDept() {
        return dept;
    }

    public void setDept(Dept dept) {
        this.dept = dept;
    }
}
package com.cwd.mybatis.bean;

import java.util.List;

/**
 * @Author Lotus_dong
 * @Date 2021-04-07 19:49
 * @function 公司部门表 部门与员工为一对多的关系
 */
public class Dept {
    private int id;
    private String name;
    private String deptDesc;
    private List<Emp> emps; //部门员工集合 一个部门对应多个员工

    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 getDeptDesc() {
        return deptDesc;
    }

    public void setDeptDesc(String deptDesc) {
        this.deptDesc = deptDesc;
    }

    public List<Emp> getEmps() {
        return emps;
    }

    public void setEmps(List<Emp> emps) {
        this.emps = emps;
    }
}

3.新建接口(EmpDao.java 和DeptDao.java)

package com.cwd.mybatis.dao;

public interface EmpDao {

}
package com.cwd.mybatis.dao;

public interface DeptDao {
    
}

4.新建mapper.xml,注意namespace接口存放路径(EmpMapper.xml和DeptMapper.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">

<!--namespace:接口存放路径 类路径-->
<mapper namespace="com.cwd.mybatis.dao.EmpDao">


</mapper>
<?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">

<!--namespace:接口存放路径 类路径-->
<mapper namespace="com.cwd.mybatis.dao.DeptDao">

</mapper>

5.在MyBatis全局配置文件中注册

<!--配置sql的映射文件-->
<mappers>
	<mapper resource="mapper/EmpMapper.xml"/>
    <mapper resource="mapper/DeptMapper.xml"/>
</mappers>

操作及测试

根据我们上面我们准备的数据库表和对应的java类,我们不难看出,如果要查询到员工(emp)或者部门(dept),最困难的地方就是(1)如何将信息封装到员工类(Emp.java)中的部门对象(private Dept dept);(2)如何将信息封装到部门类(Dept.java)中的员工集合(private List< Emp> emps)。

多表关联处理结果集

对于多表关联查询中各个Java类之间相互引用其对象的问题,MyBatis中提供的resultMap标签中有两个元素association和collection专门解决这个问题。其中association属于复杂类型联合,collection属于复杂类型集合,用于处理不同情况的问题。

association:将查询结果中属于其它类的数据关联到本类中的对象,处理一对一的结果映射,能够引用自身或凑从其它地方引用对象,比如我们这里员工所属的部门相关信息。

collection:将查询结果封装到对应类的对象集合中,处理一对多的结果映射,能够引用自身或凑从其它地方引用对象,比如我们这里的部门拥有的员工信息。

一对一结果映射

1.在接口EmpDao.java中的方法为:

//依照id查询员工信息
Emp findEmpById(int id);

2.在EmpMapper.xml文件中配置为:

	<!--多表关联查询,自己定义映射结果-->
    <resultMap id="empMap" type="Emp">
        <id column="Eid" property="id"/>
        <result column="Ename" property="name"/>
        <result column="adress" property="adress"/>
        <!--association 映射关联的对象-->
        <association property="dept" javaType="Dept">
            <id column="Did" property="id"/>
            <result column="Dname" property="name"/>
            <result column="dept_desc" property="deptDesc"/>
        </association>
    </resultMap>
    
    <select id="findEmpById" parameterType="int" resultMap="empMap">
        SELECT
          E.id AS Eid,
          E.name AS Ename,
          E.adress,
          D.id AS Did,
          D.name AS Dname,
          D.dept_desc
        FROM
          t_emp AS E
          LEFT JOIN t_dept AS D
            ON E.dept_id = D.id
        WHERE E.id = #{id}
  	</select>

注意:

​ 1.association标签映射的是关联的对象,即类中引用的其它类对象。

​ 2.association标签中的javaType属性是Java类的全限定名,如果映射到的是 HashMap,需要明确地指定 javaType 来保证行为与期望的相一致。

3.测试

@Test
public void test1() {
	try {
        //读取MyBtais全局配置文件
	 	Reader reader = Resources.getResourceAsReader("MyBatisConfig.xml");
        //创建会话工厂
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
        //建立轻量级会话
        SqlSession sqlSession = sessionFactory.openSession();
        //获取接口的代理对象
        EmpDao empDao = sqlSession.getMapper(EmpDao.class);
        //调用方法
        System.out.println(empDao.findEmpById(1));
        //递交事务
		sqlSession.commit();
        //关闭会话
		sqlSession.close();
	} catch (IOException e) {
		e.printStackTrace();
	}
}

在这里插入图片描述

一对多结果映射

1.在接口DeptDao.java中的方法为:

//按id查找部门并查找到部门所属的员工信息
Dept findDeptById(int id);

2.在DeptMapper.xml文件中配置为:

	<resultMap id="deptMap" type="Dept">
        <id column="Did" property="id"/>
        <result column="Dname" property="name"/>
        <result column="dept_desc" property="deptDesc"/>
        <!--将查询到的多个员工信息封装到员工对象,在将多个对象封装到集合-->
        <collection property="emps" javaType="list" ofType="Emp">
            <id column="Eid" property="id"/>
            <result column="Ename" property="name"/>
            <result column="adress" property="adress"/>
        </collection>
    </resultMap>

    <select id="findDeptById" parameterType="int" resultMap="deptMap">
        SELECT
          D.id AS Did,
          D.name AS Dname,
          D.dept_desc,
          E.id AS Eid,
          E.name AS Ename,
          E.adress
        FROM
          t_dept AS D
          LEFT JOIN t_emp AS E
            ON D.id = E.dept_id
        WHERE D.id = #{id}
    </select>

注意:

​ 1.collection标签用于封装查询到的多条数据信息。

​ 2.collection标签中的property属性是java类中集合属性所对应的对象名。

​ 3.collection标签中的javaType属性是所用集合的类型,如list或者set等。

​ 4.collection标签中的ofType属性是集合封装的对象类型。

3.测试

@Test
public void test1() {
	try {
        //读取MyBtais全局配置文件
	 	Reader reader = Resources.getResourceAsReader("MyBatisConfig.xml");
        //创建会话工厂
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
        //建立轻量级会话
        SqlSession sqlSession = sessionFactory.openSession();
        //获取接口的代理对象
 		DeptDao deptDao = sqlSession.getMapper(DeptDao.class);
        //调用方法
        System.out.println(deptDao.findDeptById(1));
        //递交事务
		sqlSession.commit();
        //关闭会话
		sqlSession.close();
	} catch (IOException e) {
		e.printStackTrace();
	}
}

在这里插入图片描述

懒加载

当我们在进行多张表关联查询时,我们不是每一次都要使用到所有的数据,如果每一次都把所有的数据查询出来,这会大大增加数据库的压力。使用Myabtis懒加载特性可以有效的减少数据库的压力,首次我们只查询主白表的信息,关联表的信息可以在使用时再获取加载。

懒加载的使用

MyBatis一对一关联的association和一对多的collection都可以实现懒加载。使用懒加载时要使用resultMap,不能使用resultType。MyBtais默认是没有开启懒加载的,需要在MyBtais全局配置文件中通过settings配置lazyLoadingEnabled来开启懒加载。

1.开启懒加载。

<!--mybatis的全局设置-->
<settings>
	<!--开启全局懒加载-->
	<setting name="lazyLoadTriggerMethods" value="true"/>
</settings>

在这里插入图片描述

2.配置resultMap中的collection标签或association标签。

两个表签除了它们本身的属性外,关于懒加载的属性配置方法都一样,这里以collection为例。

<!--
	配置延迟加载关联表数据
    fetchType="lazy":使用延迟加载
    select="findEmpsById":关联查询表select标签的属性id
    column="Did":查询条件,来自第一次查询的结果
-->

<collection property="emps" javaType="list" ofType="Emp" fetchType="lazy"
							select="findEmpsById" column="Did">

</collection>

案例

我们还是以部门-员工表为例,使用懒加载先查询部门信息,再查询部门下属的员工信息。

1.接口Dept.java中的方法为:

//按id查找部门并查找到部门所属的员工信息
Dept findDeptById(int id);

2.在DeptMapper.xml文件中配置为:

	<!--
        持久层框架提供的懒加载(延迟加载)功能
        MyBtais实现方式是将两张表数据分两次查询,可以设置是否使用懒加载
    -->
    <resultMap id="deptMapLazy" type="Dept">
        <id column="Did" property="id"/>
        <result column="Dname" property="name"/>
        <result column="dept_desc" property="deptDesc"/>
        <!--
            配置延迟加载关联表数据
            fetchType="lazy":使用延迟加载
            select="findEmpsById":关联查询id
            column="Did":查询条件,来自第一次查询的结果
        -->
        <collection property="emps" javaType="list" ofType="Emp" fetchType="lazy"
                    select="findEmpsById" column="Did">
            <id column="id" property="id"/>
            <result column="name" property="name"/>
            <result column="adress" property="adress"/>
        </collection>
    </resultMap>

    <!--单独查询部门信息-->
    <select id="findDeptById" parameterType="int" resultMap="deptMapLazy">
        SELECT
          D.id AS Did,
          D.name AS Dname,
          D.dept_desc
        FROM
          t_dept AS D
        WHERE D.id = #{id}
    </select>

    <!--单独查询员工信息-->
    <select id="findEmpsById" parameterType="int" resultType="Emp">
        SELECT
          id,
          name,
          adress
        FROM
          t_emp
        WHERE dept_id = #{id}
    </select>

3.测试

@Test
public void test1() {
	try {
        //读取MyBtais全局配置文件
	 	Reader reader = Resources.getResourceAsReader("MyBatisConfig.xml");
        //创建会话工厂
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
        //建立轻量级会话
        SqlSession sqlSession = sessionFactory.openSession();
        //获取接口的代理对象
 		DeptDao deptDao = sqlSession.getMapper(DeptDao.class);
        //调用方法
        Dept dept = deptDao.findDeptById(1);
        System.out.println(dept);
        System.out.println("-----------------");
        System.out.println(dept.getEmps());
        //递交事务
		sqlSession.commit();
        //关闭会话
		sqlSession.close();
	} catch (IOException e) {
		e.printStackTrace();
	}
}

在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值