转自https://blog.youkuaiyun.com/postersxu/article/details/79053916
分步查询通常应用于关联表查询,如:
电商平台,查询订单信息时需要查询部分的用户信息;
OA系统,查询个人信息时需要查询部门信息,反之亦是。
相对于关联查询来说,分步查询将查询sql拆分,这里引申出一个问题是:分步查询与关联表查询的不同。
1)关联表查询毕竟只需要查询一次数据库,对于业务量较小的系统来说,效率更高,数据库压 力相对较小;
2)分步查询虽然需要多次查询数据,但是这也意味着能够更好地使用数据缓存服务,且缓存的数据耦合度低,利用率高,而且单次查询效率很高,数据库压力较小(对于业务量较大的系统来说)
分步查询的实现(以简单的OA系统为例)
业务场景一:嵌套标签association的应用---查询员工信息时,获取员工所在的部门信息。
1.建立员工与部门实体类
/**员工实体类*/
public class Employee {
private Integer id;
private String lastName;
private String gender;
private String email;
private Department department;
private Integer dId;
getters and setters ...
}
/** 部门实体类*/
public class Department {
private Integer id;
private String departmentName;
private List<Employee> employee;
getters and setters...
}
2.一个员工对应一个部门,所以用association标签
在员工的Mapper中定义接口,并在员工Mapper.xml文件中增加对应的方法和resultMap
//员工类Mapper接口
public interface EmployeeMapper {
Employee getEmpByIdStep(Integer id);
}
//员工Mapper.xml
<!-- 分步查询映射定义 -->
<resultMap type="com.xuyong.entity.Employee" id="员工的resultMap">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="gender" property="gender"/>
<result column="email" property="email"/>
<association property="department" select="com.xuyong.dao.DepartmentDao.getDepartmentById" column="id">
</association>
</resultMap>
<!-- 使用association分布查询 -->
<select id="getEmpByIdStep" resultMap="员工的resultMap">
select * from employee where id=#{id}
</select>
property---JavaBean内成员变量名,这里就是员工实体类里定义的部门类变量名
select---指定查询方法,命名方式为对应XML文件的命名空间+查询ID(Mapper接口的包路径+方法名)
column---参数列名,指下一步查询的参数对应的列名。这里查询部门需要用到的参数为员工主键,因此此处的column应为员工主键的数据库列名
3.定义部门Mapper接口,与上文中的select值相对应
/**
* 部门类Mapper接口
*/
public interface DepartmentDao {
/**
* 根据部门主键获取部门信息
*/
Department getDepartmentById(Integer id);
}
相应的xml编写
这里直接使用resultType进行映射,开发中建议使用resultMap
<!-- 根据主键获取部门信息 -->
<select id="getDepartmentById" resultType="com.xuyong.entity.Department">
select id,department_name from department where id=#{id}
</select>
以下为查询结果,可以看到打印了两次sql
[com.xuyong.dao.EmployeeDaoPlus.getEmpByIdStep]-==> Preparing: select * from employee where id=?
[com.xuyong.dao.EmployeeDaoPlus.getEmpByIdStep]-==> Parameters: 1(Integer)
[com.xuyong.dao.EmployeeDaoPlus.getEmpByIdStep]-<== Columns: id, last_name, gender, email, d_id
[com.xuyong.dao.EmployeeDaoPlus.getEmpByIdStep]-<== Row: 1, 徐永, 男, t1heluosh1@163.com, 1
[com.xuyong.dao.DepartmentDao.getDepartmentById]-====> Preparing: select id,department_name from department where id=?
[com.xuyong.dao.DepartmentDao.getDepartmentById]-====> Parameters: 1(Integer)
[com.xuyong.dao.DepartmentDao.getDepartmentById]-<==== Columns: id, department_name
[com.xuyong.dao.DepartmentDao.getDepartmentById]-<==== Row: 1, 文案部
[com.xuyong.dao.DepartmentDao.getDepartmentById]-<==== Total: 1
[com.xuyong.dao.EmployeeDaoPlus.getEmpByIdStep]-<== Total: 1
Employee [dId=1, department=Department [departmentName=文案部, employee=null, id=1], email=t1heluosh1@163.com, gender=男, id=1, lastName=徐永]
二、延迟加载
什么是延迟加载?
当我们在某项业务里需要同时获取A、B两份数据,但是B这份数据又不需要立即使用(或者存在压根就不会使用的情况),当程序需要加载B时,再去请求数据库来获取B数据,而不是一次性将数据全部取出来或者重新发送一份请求,这就是延迟加载。
MyBatis默认关闭延迟加载技术,需要我们在配置文件里手动配置,配置如下:
<!-- 设置 -->
<settings>
<!-- 驼峰命名映射 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<setting name="jdbcTypeForNull" value="NULL"/>
<!-- 懒加载设置 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 侵入懒加载,设置为false则按需加载,否则会全部加载 -->
<setting name="aggressiveLazyLoading" value="false"/>
<!-- 标准日志输出 -->
<!--<setting name="logImpl" value="STDOUT_LOGGING"/>-->
<!-- log4j日志输出 -->
<setting name="logImpl" value="LOG4J"/>
</settings>
注意:
1. lazyLoadingEnabled与aggressiveLazyLoading必须全部设置,且lazyLoadingEnabled为true,aggressiveLazyLoading为false才能让延迟加载真正生效
2. toString与重载方法过滤:
通常我们在测试时会在实体类加入toString,或者存在了一些重载方法,这些MyBatis会对其进行过滤,但是过滤会调 用cglib与asm指定包,因此要将两个包添加到buildpath。以下为两个包的maven依赖:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.1</version>
</dependency>
<dependency>
<groupId>asm</groupId>
<artifactId>asm</artifactId>
<version>3.3.1</version>
</dependency>
3.如果想单个开启或禁用延迟加载,可以使用fetchType属性来实现
<!-- collection分布查询 -->
<resultMap type="com.xuyong.entity.Department" id="DepartAndEmpByStep">
<id column="id" property="id"/>
<result column="department_name" property="departmentName"/>
<!-- 多个值传递可封装成map如:column="{key1=column1,...}" fetchType="lazy" 表示使用懒加载 fetchType="eager"表示禁用懒加载 -->
<collection property="employee" select="com.xuyong.dao.EmployeeDaoPlus.getEmpByDid"
column="{id=id}" fetchType="lazy">
</collection>
</resultMap>