首先我们先创建两个表和pojo用来演示多表查询,如下:
用户表:t_emp表,其中emp_id为自增主键。
t_emp对应的pojo:Employee
public class Employee { private Integer empId; private String empName; private Integer age; private String gender; private Dept dept; public Employee(Integer empId, String empName, Integer age, String gender, Dept dept) { this.empId = empId; this.empName = empName; this.age = age; this.gender = gender; this.dept = dept; } public Employee() { } //Setter and Getter方法必须要有,这里不在展示 @Override public String toString() { return "Employee{" + "empId=" + empId + ", empName='" + empName + '\'' + ", age=" + age + ", gender='" + gender + '\'' + ", dept=" + dept + '}'; } }
部门表:t_dept,dept_id为主键
对应的pojo:Dept
public class Dept { private Integer deptId; private String deptName; private Collection<Employee> employees; public Dept(Integer deptId, String deptName) { this.deptId = deptId; this.deptName = deptName; } public Dept() { } //Setter and Getter方法必须要有,这里不在展示 @Override public String toString() { return "Dept{" + "deptId=" + deptId + ", deptName='" + deptName + '\'' + ", employees=" + employees + '}'; } }
在核心配置文件中的Setting的配置
<settings> <!--将下滑线映射为驼峰--> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings>
IDEA中的目录
多对一查询
多对一查询:第一张表中的多条记录可能对应第二张表中的一条记录,并且在第一张表对应的类中存在第二张表对应类的实例变量。对应到例子中,员工表中,不同的员工肯属于同一个部门,员工表中有部门类型的实例。这里提供三种查询方式。
使用级联处理映射关系
在EmployeeMapper.xml中:
<!-- resultMap的属性: id:该映射的唯一标识 type:处理映射关系的实体类的类型。 内部标签: id:处理主键和实体类属性的映射关系 result:处理普通字段和实体类属性的映射关系 column:字段名 property:映射的属性名 --> <resultMap id="empAndDeptResultMapOne" type="pojo.Employee"> <id column="emp_id" property="empId"/> <result column="emp_name" property="empName"/> <result column="age" property="age"/> <result column="gender" property="gender"/> <result column="dept_id" property="dept.deptId"/> <result column="dept_name" property="dept.deptName"/> </resultMap> <select id="getAllEmpAndDeptOne" resultMap="empAndDeptResultMapOne"> SELECT t_emp.*,t_dept.dept_name FROM t_emp LEFT JOIN t_dept ON t_dept.dept_id = t_emp.dept_id; </select>
使用asscoiation处理映射关系
在EmployeeMapper.xml中:
<resultMap id="empAndDeptResultMapTwo" type="pojo.Employee"> <id column="emp_id" property="empId"/> <result column="emp_name" property="empName">/</result> <result column="age" property="age"/> <result column="gender" property="gender"/> <!-- association:处理多对一的映射关系(处理实体类型的属性) property:要处理的实体类型的属性 javaType:该属性的实体类型 --> <association property="dept" javaType="pojo.Dept"> <id column="dept_id" property="deptId"/> <result column="dept_name" property="deptName"/> </association> </resultMap> <select id="getAllEmpAndDeptTwo" resultMap="empAndDeptResultMapTwo"> SELECT t_emp.*,t_dept.dept_name FROM t_emp LEFT JOIN t_dept ON t_dept.dept_id = t_emp.dept_id; </select>
使用分步查询处理映射关系
在DeptMapper中:
<!--在本次分步查询中属于第二步查询--> <select id="getDeptById" resultType="pojo.Dept"> select *from t_dept where dept_id = #{dept_id}; </select>
在EmployeeMapper.xml中:
<resultMap id="empAndDeptResultMapByStep" type="pojo.Employee"> <id column="emp_id" property="empId"/> <result column="emp_name" property="empName"/> <result column="age" property="age"/> <result column="gender" property="gender"/> <!-- 使用分步查询对某一属性进行映射 property:需要进行映射处理的实体类属性 select:分步查询的唯一标识 column:分步查询所需要的参数(由第一次查询提供) dept属性由第二步查询来获取 --> <association property="dept" select="mapper.DeptMapper.getDeptById" column="dept_id"/> </resultMap> <select id="getAllEmpAndDeptByStep" resultMap="empAndDeptResultMapByStep"> select *from t_emp; </select>
一对多查询
一对多查询:第一张表中的一条记录对应第二张表中的多条记录,并且在第一张表对应类中有第二张表对应类中的实例集合属性。对应到例子当,一个部门可能会有多个员工,相应的部门表对应类中有List集合类型的属性。这里提供两种查询方式。
使用collection处理映射关系
在DeptMapper中:
<resultMap id="deptAndEmpResultMap" type="pojo.Dept"> <id column="dept_id" property="deptId"/> <result column="dept_name" property="deptName"/> <!--collection:用于处理一对多的查询--> <collection property="employees" ofType="pojo.Employee"> <id column="emp_id" property="empId"/> <result column="emp_name" property="empName"/> <result column="age" property="age"/> <result column="gender" property="gender"/> </collection> </resultMap> <select id="getAllDeptAndEmp" resultMap="deptAndEmpResultMap"> SELECT *FROM t_dept LEFT JOIN t_emp ON t_dept.dept_id = t_emp.dept_id ; </select>
使用分步查询处理映射关系
在EmployeeMapper.xml中:
<select id="getEmpByDept" resultType="pojo.Employee"> select *from t_emp where dept_id = #{dept_id}; </select>
在DeptMapper中:
<resultMap id="deptAndEmpByStepResultMap" type="pojo.Dept"> <id column="dept_id" property="deptId"/> <result column="dept_name" property="deptName"/> <collection property="employees" ofType="Employee" select="mapper.EmpMapper.getEmpByDept" column="dept_id"/> </resultMap> <!-- List<Dept> getAllDeptAndEmpByStep();--> <select id="getAllDeptAndEmpByStep" resultMap="deptAndEmpByStepResultMap"> SELECT *FROM t_dept ; </select>
分步查询的延迟加载和按需加载
在mybatis的核心配置文件当中可以配置这两种属性。
<settings> <!--延迟加载的全局开关,默认为false关闭。当开启时,所有关联对象都会延迟加载--> <setting name="lazyLoadingEnabled" value="false"/> <!--完整加载的全局开关,在mybatis版本大于3.4.1时,默认为false关闭,为按需加载。在启用时,延迟加载的任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载--> <setting name="aggressiveLazyLoading" value="false"/> </settings>
按需加载:按需加载又称懒加载,在需要时才会去加载。我们以多对一的分步查询为例。
@Test public void testGetAllEmpAndDeptByStep(){ SqlSession sqlSession = SqlSessionUtils.getSqlSession(); EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class); List<Employee> employeeList = empMapper.getAllEmpAndDeptByStep(); employeeList.forEach((Employee employee) ->{ System.out.println(employee.getEmpName()); }); }
我们在设置属性lazyLoadingEnabled之前:
编译后的日志记录如下:
尽管我们只需要emp_name,仅通过select * from t_emp就可以将所需数据查出来,但是我们还是进行了两步查询。
我们将属性lazyLoadingEnabled设置为true,aggressiveLazyLoading默认为false,所以此时是按需加载。
编译后的日志记录如下:
因为需要的对象仅通过select * from t_emp就能得出,所以只会进行这一次查询。
此时我们再将属性aggressiveLazyLoading设置为true,编译后的日志记录如下:
此时不在是按需加载,而是完整加载。
在按需加载时,我们可以在collection标签或者是association标签中设置的fetchType属性设置当前的分步查询是否使用延迟加载, fetchType="lazy(延迟加载)|eager(立即加载)