MyBatis的使用七(处理表与表之间的关系)
本文主要讲述mybatis的处理表与表之间的关系
一. 介绍t_emp和t_dept表
1. t_emp表结构
2. t_dept表结构
二. 数据表的关系
1. 阐明关系
一个部门可以有多个员工,但是一个员工只能属于一个部门
2. 实体类pojo的声明
1) Employee类的声明如下
public class Employee { private Integer empId; private String empName; private Integer age; private String gender; private Department dept; public Employee() { } get和set()方法... @Override public String toString() { return "Employee{" + "empId=" + empId + ", empName='" + empName + '\'' + ", age=" + age + ", gender='" + gender + '\'' + ", dept=" + dept + '}'; } }
由于一个员工只能属于一个部门,因此在Employee中才会有 private Department dept 成员变量。
2)Department类声明如下
public class Department { private Integer deptId; private String deptName; private List<Employee> employees; public Department() { } get和set()方法... @Override public String toString() { return "Department{" + "deptId=" + deptId + ", deptName='" + deptName + '\'' + ", employees=" + employees + '}'; } }
由于一个部门可以有多个员工,因此在Department类中才会有 private List<Employee> employees 成员变量
三. 使用mybatis解决多对一【一对多】
1. 多对一的关系
这里的多对一的关系是指:多个员工在一个部门,思考的角度是站在员工Employee类的。
1)提出问题:
当需要查询单个员工信息以及所在部门时,应该如何处理?
EmpMapper接口声明如下
public interface EmpMapper { // 根据id查询员工信息 Employee selectEmpAndDept(@Param("id") int id); }
2)处理方式:
方式1:采用级联的方式
EmpMapper.xml文件声明如下
<!--// 根据id查询员工信息 Employee selectEmpAndDept(@Param("id") int id);--> <!--使用: resultType="Employee"结果: Employee{empId=4, empName='赵六', age=24, gender='男', dept=null}--> <!--级联--> <resultMap id="resultEmpAndDept1" type="Employee"> <id column="emp_id" property="empId" /> <result column="emp_name" property="empName" /> <result column="dept_id" property="dept.deptId" /> <result column="dept_name" property="dept.deptName" /> </resultMap>
<select id="selectEmpAndDept" resultMap="resultEmpAndDept1"> SELECT t_emp.*,t_dept.`dept_name` FROM t_emp,t_dept WHERE t_emp.dept_id = t_dept.dept_id and emp_id = #{id} </select>
前面提过resultMap可以自定义映射名,下面阐述<resultMap>标签
<!-- resultMap: id:resultMap标签的唯一标识 type:返回类型 <id>:表t_emp的主键的字段名 <result>:表t_emp的非主键的字段名 其中 <result column="dept_id" property="dept.deptId" /> column:字段名dept_id,property:实体类的属性名dept.deptId 意义是将查询到的部门信息--封装--》Department对象 --赋值--》Employee类中的dept成员变量 --> <resultMap id="resultEmpAndDept1" type="Employee"> <id column="emp_id" property="empId" /> <result column="emp_name" property="empName" /> <result column="dept_id" property="dept.deptId" /> <result column="dept_name" property="dept.deptName" /> </resultMap>
注意:<result column="dept_id" property="dept.deptId" />,是Employee类的成员对象dept . 属性名
方式2:采用 association 标签
EmpMapper.xml文件声明如下
<resultMap id="resultEmpAndDept2" type="Employee"> <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:是设置要处理属性的类型名称--> <association property="dept" javaType="Department"> <id column="dept_id" property="deptId"></id> <id column="dept_name" property="deptName"></id> </association> </resultMap> <select id="selectEmpAndDept" resultMap="resultEmpAndDept2"> SELECT t_emp.*,t_dept.`dept_name` FROM t_emp,t_dept WHERE t_emp.dept_id = t_dept.dept_id and emp_id = #{id} </select>
下面阐述<association>标签 的内容
<!--association的 property:是需要处理映射关系的属性名,如dept javaType:是设置要处理属性的类型名称--> <association property="dept" javaType="Department"> <id column="dept_id" property="deptId"></id> <id column="dept_name" property="deptName"></id> </association>
方式3:采用分步查询
先根据员工编号,查询员工信息,在根据员工信息中的部门编号,查询相对应的部门信息
因此,需要创建DeptMapper接口,DeptMapper接口的声明如下
public interface DeptMapper { // 根据id查询部门 Department selectDeptById(@Param("id") int id); }
DeptMapper.xml文件声明如下
<!--namespace绑定mapper的接口所在的包名.接口名--> <mapper namespace="com.hspedu.mapper.DeptMapper"> <!--// 根据id查询部门 Department selectDeptById(@Param("id") int id);--> <select id="selectDeptById" resultType="Department"> select * from t_dept where dept_id = #{id} </select> </mapper>
EmpMapper.xml文件声明如下
<resultMap id="resultEmpAndDept3" type="Employee"> <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:设置需要处理映射关系的属性的属性名 select:设置分步查询的sql语句的唯一标识 column:将select标签中查询出的某一个字段作为分步查询的条件 fetchType:是否开启延迟加载【针对某一个sql语句】eager立刻,lazy延迟 --> <association property="dept" fetchType="eager" select="com.hspedu.mapper.DeptMapper.selectDeptById" column="dept_id"> </association> </resultMap> <select id="selectEmpAndDeptThree" resultMap="resultEmpAndDept3" > select * from t_emp where emp_id = #{id} </select>
下面阐述 <association> 标签的内容
<!--association的 property:设置需要处理映射关系的属性的属性名 select:设置分步查询的sql语句的唯一标识 column:将select标签中查询出的某一个字段作为分步查询的条件 fetchType:在方法中是否开启延迟加载【针对某一个sql语句】eager立刻,lazy延迟 --> <association property="dept" fetchType="eager" select="com.hspedu.mapper.DeptMapper.selectDeptById" column="dept_id"> </association>
注意:association标签中的select的意义,
在t_emp表中查询到的员工信息字段中的dept_id值作为参数,传入到DeptMapper接口的selectDeptById()方法中,
在t_dept表中按照部门编号dept_id查询相对应的部门信息。
分步查询 插入一个知识点:延迟加载
a> 引入延迟加载
当我们只是想查询员工信息的姓名时,例如
@Test // 查询指定id的员工信息 public void test02(){ Employee employee = mapper.selectEmpAndDept(4); System.out.println(employee.getEmpName()); }
如果没有延迟加载的话,执行结果如下
DEBUG 02-04 15:37:05,646 ==> Parameters: 4(Integer) (BaseJdbcLogger.java:137) DEBUG 02-04 15:37:05,669 ====> Preparing: select * from t_dept where dept_id = ? (BaseJdbcLogger.java:137) DEBUG 02-04 15:37:05,669 ====> Parameters: 100(Integer) (BaseJdbcLogger.java:137) DEBUG 02-04 15:37:05,675 <==== Total: 1 (BaseJdbcLogger.java:137) DEBUG 02-04 15:37:05,677 <== Total: 1 (BaseJdbcLogger.java:137) 赵六
即执行了查询员工信息的sql语句,又执行了查询部门信息的sql语句,如何让它不执行查询部门信息的sql语句呢?
b> 延迟加载的配置
在mybatis-config.xml文件中添加如下setting标签,引入延迟加载
<settings> <!--将MySQL中_映射为java的驼峰--> <setting name="mapUnderscoreToCamelCase" value="true"/> <!-- 开启延时加载【懒加载】--> <setting name="lazyLoadingEnabled" value="true"/> <!--强制加载所有的懒配置--> <setting name="aggressiveLazyLoading" value="false"/> </settings>
并将<association fetchType="eager"> 的 fetchType = "lazy",即在执行该sql语句时,使用延迟加载,执行结果如下
DEBUG 02-04 15:42:26,231 ==> Preparing: select * from t_emp where emp_id = ? (BaseJdbcLogger.java:137) DEBUG 02-04 15:42:26,261 ==> Parameters: 4(Integer) (BaseJdbcLogger.java:137) DEBUG 02-04 15:42:26,322 <== Total: 1 (BaseJdbcLogger.java:137) 赵六
只执行了查询员工信息的sql语句,说明延迟加载有了效果。
c> 延迟加载总结:
引入延迟加载,在mybatis配置文件中添加setting标签......【全局 延迟加载】
如果对于某一个sql语句,不想使用延迟加载,则<association fetchType="eager"> 【局部 不使用延迟加载】
3)总结:
根据传入的emp_id在t_emp表中查询员工信息,员工信息的字段中包含有dept_id,然后依据dept_id,在t_dept表中查询相应的部门信息,
将部门信息--反射-->Department类的对象--赋值-->Employee类的成员变量dept。
2. 一对多关系
这里的一对多是一个部门可以有多个员工,是在Department类角度考虑的。
1)提出问题
按照部门编号查询一个部门有多少员工?
DeptMapper接口声明如下
public interface DeptMapper { // 查询一个部门有多少员工 Department selectDeptAndEmp(@Param("id") int id); }
2)处理方式
方式1:使用collection标签
DeptMapper.xml文件声明如下
<!--// 查询一个部门有多少员工 Department selectDeptAndEmp(@Param("id") int id);--> <resultMap id="resultDeptAndEmp1" type="Department"> <id column="dept_id" property="deptId"></id> <result column="dept_name" property="deptName"></result> <collection property="employees" ofType="Employee"> <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> </collection> </resultMap> <select id="selectDeptAndEmp" resultMap="resultDeptAndEmp1"> SELECT t_emp.*,t_dept.* FROM t_emp,t_dept WHERE t_emp.`dept_id` = t_dept.`dept_id` and t_dept.dept_id = #{id} </select>
下面阐述collection标签
<collection property="employees" ofType="Employee"> <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> </collection>
property:实体类的成员变量【属性】,ofType:表示将查询的结果--封装-->实体类对象--装入-->集合
方式2:分步查询
第一步:在t_dept表中根据传入的dept_id查询部门信息
第二步:在t_emp中根据第一步中得到的dept_id查询员工信息
EmpMapper接口的声明如下
public interface EmpMapper { // 根据部门编号查询员工信息 List<Employee> selectEmployeesByDeptId(@Param("id") int id); }
EmpMapper.xml文件声明如下
<!-- // 根据部门编号查询员工信息 List<Employee> selectEmployeesByDeptId(@Param("id") int id);--> <resultMap id="resultEmployeesByDeptId" type="Employee"> <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> </resultMap> <select id="selectEmployeesByDeptId" resultMap="resultEmployeesByDeptId"> select * from t_emp where dept_id = #{id} </select>
DeptMapper.xml文件声明如下
<resultMap id="resultDeptAndEmp" type="Department"> <id column="dept_id" property="deptId"></id> <result column="dept_name" property="deptName"></result> <association property="employees" fetchType="lazy" select="com.hspedu.mapper.EmpMapper.selectEmployeesByDeptId" column="dept_id"></association> </resultMap> <select id="selectDeptAndEmpTwo" resultMap="resultDeptAndEmp"> select * from t_dept where dept_id = #{id} </select>
使用association标签,
property:实体类需要映射处理的成员名【属性】;
select:调用EmpMapper接口的 selectEmployeesByDeptId()方法,即 根据得到的部门信息中的dept_id,在t_emp表中查询该部门的所有员工信息。
3)总结
由dept_id在t_emp查询的结果集--反射-->Employee对象--装入-->Employee集合--赋值-->Department类的employees成员变量。