mybatis复习04高级查询 一对多,多对一的映射处理
一对多,和多对一之间的关系。比如,当前有一个实体类为员工类,还有一个实体类为部门类,那么由日常生活便能看出他们之间的关系,一个员工只属于一个部门,然后一个部门包括多个员工。
创建数据库表 员工和部门
员工表 t_emp:
部门表t_dept:
填充一些测试的数据:
抽象对应的实体类
员工实体类
由一开始的分析得出,员工类应包含员工个人的基本信息,以及对应部门的基本信息。所以在员工类中,应包含部门这一属性,部门也是一个单独的对象,包括部门号和部门名称。
确定了类中的基本属性后,为其添加对应的构造器,这里需要注意的是,员工类的构造器应只具备其员工个人基本属性的构造器,然后添加对应的getter和setter方法以及重写toString方法。
package com.gothic.sunset.pojo;
public class Emp {
private Integer empId;//员工号
private String empName;//员工姓名
private Integer age;//员工年龄
private String gender;//员工性别
private Dept dept;//部门
public Emp() {
}
public Emp(Integer empId, String empName, Integer age, String gender) {
this.empId = empId;
this.empName = empName;
this.age = age;
this.gender = gender;
}
public Integer getEmpId() {
return empId;
}
public void setEmpId(Integer empId) {
this.empId = empId;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public Dept getDept() {
return dept;
}
public void setDept(Dept dept) {
this.dept = dept;
}
@Override
public String toString() {
return "Emp{" +
"empId=" + empId +
", empName='" + empName + '\'' +
", age=" + age +
", gender='" + gender + '\'' +
", dept=" + dept +
'}';
}
}
部门实体类
部门实体类应包括部门的基本信息和每个部门中所有员工的信息。同理,其构造器也需要注意,只具备部门基本信息的构造器。
package com.gothic.sunset.pojo;
import java.util.List;
public class Dept {
private Integer deptId;//部门id号
private String deptName;//部门名称
private List<Emp> emps;//每个部门中的所有员工
public Dept() {
}
public Dept(Integer deptId, String deptName) {
this.deptId = deptId;
this.deptName = deptName;
}
public Integer getDeptId() {
return deptId;
}
public void setDeptId(Integer deptId) {
this.deptId = deptId;
}
public String getDeptName() {
return deptName;
}
public void setDeptName(String deptName) {
this.deptName = deptName;
}
public List<Emp> getEmps() {
return emps;
}
public void setEmps(List<Emp> emps) {
this.emps = emps;
}
@Override
public String toString() {
return "Dept{" +
"deptId=" + deptId +
", deptName='" + deptName + '\'' +
", emps=" + emps +
'}';
}
}
多对一的映射处理
级联方式resultMap自定义映射处理
需求:根据员工id,查询员工的所有信息(包括他的基本信息和对应的部门信息)。
首先,在mapper接口中编写对应的方法:
/**
* 根据id查询员工信息
* @param empId
* @return
*/
Emp getEmpByEmpId(@Param("empId") Integer empId);
在映射文件xml中编写对应的sql:
<resultMap id="getEmpAllByEmpId" type="Emp">
<id column="emp_id" property="empId"></id>
<result column="emp_name" property="empName"></result>
<result column="age" property="age"></result>
<result column="gender" property="gender"></result>
<result column="dept_id" property="dept.deptId"></result>
<result column="dept_name" property="dept.deptName"></result>
</resultMap>
<!-- Emp getEmpByEmpId(@Param("empId") Integer empId);-->
<select id="getEmpByEmpId" resultMap="getEmpAllByEmpId">
select
t_emp.*,t_dept.*
from t_emp
inner join t_dept
on t_emp.dept_id = t_dept.dept_id
where t_emp.emp_id = #{empId}
</select>
这里通过使用resultMap自定义映射关系来将一对多映射处理,然后sql的话使用两表内连接查询。
测试用例:
@Test
public void testGetEmpByEmpIdList(){
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
Emp emp = mapper.getEmpByEmpId(1);
System.out.println(emp);
}
输出:
使用resultMap中的association标签
association标签级联查询
association:处理多对一的映射关系。
association标签的结构:
- property:需要处理多对一的映射关系的属性名
- javaType:该属性的类型
<association property="" javaType="" ......>
<id property="" column=""></id>
<result property="" column=""></result>
</association>
其中association的属性还包括很多,下图是association标签中的属性:
在mapper接口中编写一个新的根据id查询的方法:
/**
* 根据id查询员工信息 使用association标签 级联查询
* @param empId
* @return
*/
Emp getEmpByEmpIdTwo(@Param("empId") Integer empId);
在映射文件xml中编写对应的sql:
<resultMap id="getEmpAllByEmpIdTwo" type="Emp">
<id column="emp_id" property="empId"></id>
<result column="emp_name" property="empName"></result>
<result column="age" property="age"></result>
<result column="gender" property="gender"></result>
<association property="dept" javaType="Dept" >
<id column="dept_id" property="deptId"></id>
<result column="dept_name" property="deptName"></result>
</association>
</resultMap>
<!--Emp getEmpByEmpIdTwo(@Param("empId") Integer empId);-->
<select id="getEmpByEmpIdTwo" resultMap="getEmpAllByEmpIdTwo">
select
t_emp.*,t_dept.*
from t_emp
inner join t_dept
on t_emp.dept_id = t_dept.dept_id
where t_emp.emp_id = #{empId}
</select>
测试用例:
@Test
public void testGetEmpByEmpIdListTwo(){
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
Emp emp = mapper.getEmpByEmpIdTwo(1);
System.out.println(emp);
}
输出:
association标签分步查询
分步查询,涉及到association标签的另外两个属性:
- select:设置分布查询的sql的唯一标识(namespace.SQLId或mapper接口的全类名.方法名)
- column:设置分步查询的条件
同样编写一个新的mapper接口中的方法,来进行测试:
/**
* 根据id查询员工信息 使用association标签 分步查询
* @param empId
* @return
*/
Emp getEmpByEmpIdThree(@Param("empId") Integer empId);
在映射文件xml中编写对应的sql:
<resultMap id="getEmpAndDeptByStepMap" type="Emp">
<id column="emp_id" property="empId"></id>
<result column="emp_name" property="empName"></result>
<result column="age" property="age"></result>
<result column="gender" property="gender"></result>
<association column="dept_id" property="dept" select="com.gothic.sunset.mapper.EmpMapper.getEmpAndDeptByStepTwo" >
</association>
</resultMap>
<!-- Emp getEmpAndDeptByStepOne(@Param("empId") Integer empId);-->
<select id="getEmpAndDeptByStepOne" resultMap="getEmpAndDeptByStepMap">
select * from t_emp where emp_id = #{empId}
</select>
<resultMap id="getEmpAndDeptByStepTwoMap" type="Dept">
<id column="dept_id" property="deptId"></id>
<result column="dept_name" property="deptName"></result>
</resultMap>
<!--Dept getEmpAndDeptByStepTwo(@Param("deptId") Integer deptId);-->
<select id="getEmpAndDeptByStepTwo" resultMap="getEmpAndDeptByStepTwoMap">
select * from t_dept where dept_id = #{deptId}
</select>
测试用例:
@Test
public void getEmpAndDeptByStep(){
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
Emp emp = mapper.getEmpAndDeptByStepOne(1);
System.out.println(emp);
}
输出:
一对多的映射处理
resultMap中的collection标签
collection:
collection:用来处理一对多的映射关系
- property:需要处理一对多的映射关系的属性名
-
ofType:表示该属性对应的集合中存储的数据的类型
其完整的参数列表:
需求:根据部门id查询部门中的所有员工。
collection标签级联查询
首先,在mapper接口中编写对应的方法:
这里,根据id查询部门表返回所有的员工及部门信息,返回值应该是一个list而且其泛型要遵循Dept类的约束(思考一下就会明白,为什么不是遵循Emp类的约束)。
/**
* 根据部门id查询所有员工信息
* @param deptId
* @return
*/
List<Dept> getDeptAndEmpById(Integer deptId);
在映射文件xml中编写对应的sql:
<resultMap id="getDeptAndEmpMap" type="Dept">
<id property="deptId" column="dept_id"></id>
<result property="deptName" column="dept_name"></result>
<collection property="emps" ofType="Emp">
<id property="empId" column="emp_id"></id>
<result property="empName" column="emp_name"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
<result property="email" column="email"></result>
</collection>
</resultMap>
<!--List<Dept> getDeptAndEmpById(Integer deptId);-->
<select id="getDeptAndEmpById" resultMap="getDeptAndEmpMap">
select * from t_dept left join t_emp on t_dept.dept_id = t_emp.emp_id where t_dept.dept_id = #{dept_id}
</select>
测试用例:
@Test
public void getDeptAndEmpById(){
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
List<Dept> emps = mapper.getDeptAndEmpById(1);
emps.forEach(System.out::println);
}
输出:
collection标签分步查询
分步查询可以分为两步:
- 分步查询第一步:查询部门信息
-
分步查询第二步:根据部门id查询部门中的所有员工
在mapper接口中编写对应的新方法:
/**
* 通过分步查询,查询部门及对应的所有员工信息
* 分步查询第一步:查询部门信息
* @param deptId
* @return
*/
Dept getDeptAndEmpByStepOne(@Param("deptId") Integer deptId);
/**
* 通过分步查询,查询部门及对应的所有员工信息
* 分步查询第二步:根据部门id查询部门中的所有员工
* @param empId
* @return
*/
List<Emp> getDeptAndEmpByStepTwo(@Param("empId") Integer empId);
在映射文件xml中编写对应的sql:
<resultMap id="getDeptAndEmpByStepOneMap" type="Dept">
<id property="deptId" column="dept_id"></id>
<result property="deptName" column="dept_name"></result>
<collection property="emps"
select="com.gothic.sunset.mapper.DeptMapper.getDeptAndEmpByStepTwo"
column="dept_id">
</collection>
</resultMap>
<!--Dept getDeptAndEmpByStepOne(@Param("deptId") Integer deptId);-->
<select id="getDeptAndEmpByStepOne" resultMap="getDeptAndEmpByStepOneMap">
select * from t_dept where dept_id = #{deptId}
</select>
<!--List<Emp> getDeptAndEmpByStepTwo(@Param("empId") Integer empId);-->
<select id="getDeptAndEmpByStepTwo" resultType="Emp">
select * from t_emp where emp_id = #{empId}
</select>
测试用例:
这里因为我是恰好部门id为1的员工只有一条数据,所以就这样写了。大家如果单纯为了查看是否正常可以不输出,从DEBUG中就可以看到。
@Test
public void getDeptAndEmpByStrp(){
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
Dept emps = mapper.getDeptAndEmpByStepOne(1);
System.out.println(emps);
}
输出:
补充延迟加载
分步查询的优点:可以实现延迟加载,但是必须在核心配置文件中设置全局配置信息:
- lazyLoadingEnabled:延迟加载的全局开关。当开启时,所有关联对象都会延迟加载
-
aggressiveLazyLoading:当开启时,任何方法的调用都会加载该对象的所有属性。 否则,每个属性会按需加载
此时就可以实现按需加载,获取的数据是什么,就只会执行相应的sql。此时可通过association和collection中的fetchType属性设置当前的分步查询是否使用延迟加载,fetchType=“lazy(延迟加载)|eager(立即加载)”.
<settings>
<!--开启延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
fetchType:当开启了全局的延迟加载之后,可以通过该属性手动控制延迟加载的效果,fetchType=”lazy(延迟加载)|eager(立即加载)
,它作用于collection和association标签上。
可以自行思考一下,开启延迟加载后的状况。
今天的复习结束啦!!!!