多表关联查询处理结果集
我们使用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();
}
}