MyBatis学习——分步查询与延迟加载
声明:面试是遇到延迟加载问题,在网页搜索到此篇文章,感觉很有帮助,留此学习之用!
一、分步查询
分步查询通常应用于关联表查询,如:电商平台,查询订单信息时需要查询部分的用户信息;OA系统查询个人信息时需要查询部门信息,反之亦是。相对于关联查询来说,分步查询将查询sql拆分,这里引申出一个问题是:分步查询与关联表查询的不同。
从代码层面来说:关联表查询能够有效的简化代码编写逻辑,减小代码编写难度,同时避免B-U-G(代码多了,bug 就多了);
而分步查询则能够增强代码的可用性(这点我也不是非常理解,在实际开发中也未遇到过)
从功能上说:关联表查询毕竟只需要查询一次数据库,对于业务量较小的系统来说,效率更高,数据库压 力相对较小;
分步查询虽然需要多次查询数据,但是这也意味着能够更好地使用数据缓存服务,且缓存的 数据耦合度低,利用率高,而且单次查询效率很高,数据库压力较小(对于业务量较大的系 统来说)。还有一点则是数据库锁的问题,毕竟关联查询是多表同时使用,分步查询每次只 操作一个表。
分步查询的实现(以简单的OA系统为例)
业务场景一:嵌套标签association的应用---查询员工信息时,获取员工所在的部门信息。
1.建立员工与部门实体类
/**
* 员工实体类
*
* @author xuyong
*
*/
public class Employee { private Integer id; private String lastName; private String gender; private String email; private Department department; private Integer dId; public Integer getdId() {
return dId;
} public void setdId(Integer dId) {
this.dId = dId;
} public Integer getId() {
return id;
} public void setId(Integer id) {
this.id = id;
} public String getLastName() {
return lastName;
} public void setLastName(String lastName) {
this.lastName = lastName;
} public String getGender() {
return gender;
} public void setGender(String gender) {
this.gender = gender;
} public String getEmail() {
return email;
} public void setEmail(String email) {
this.email = email;
} public Department getDepartment() {
return department;
} public void setDepartment(Department department) {
this.department = department;
} @Override
public String toString() {
return "Employee [dId=" + dId + ", department=" + department
+ ", email=" + email + ", gender=" + gender + ", id=" + id
+ ", lastName=" + lastName + "]";
}
}
/**
* 部门实体类
*
* @author xuyong
*
*/
public class Department { private Integer id; private String departmentName; private List<Employee> employee; public List<Employee> getEmployee() {
return employee;
} public void setEmployee(List<Employee> employee) {
this.employee = employee;
} /**
*
*/
private Department() {
super();
// TODO Auto-generated constructor stub
} /**
* @param id
* @param departmentName
*/
private Department(Integer id, String departmentName) {
super();
this.id = id;
this.departmentName = departmentName;
} public Integer getId() {
return id;
} public void setId(Integer id) {
this.id = id;
} public String getDepartmentName() {
return departmentName;
} public void setDepartmentName(String departmentName) {
this.departmentName = departmentName;
} @Override
public String toString() {
return "Department [departmentName=" + departmentName + ", employee="
+ employee + ", id=" + id + "]";
} }
2.JUNIT单元测试编写
获取sqlSession实例(未使用框架,自己手动获取)
/**
* 获取sqlSession实例
* @return
* @throws IOException
*/
public SqlSessionFactory getSessionFactory() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
return new SqlSessionFactoryBuilder().build(inputStream);
}
业务方法
/**
* 分布查询实现:
* 获取员工信息及部门信息
* @throws IOException
*/
@Test
public void test08() throws IOException {
SqlSessionFactory sessionFactory = getSessionFactory();
SqlSession sqlSession = sessionFactory.openSession();
try {
EmployeeDaoPlus dao = sqlSession.getMapper(EmployeeDaoPlus.class);
Employee employee = dao.getEmpByIdStep(1);
System.out.println(employee);
} catch (Exception e) {
e.printStackTrace();
} finally {
sqlSession.close();
}
}
在Mapper中定义接口
/**
* 员工类Mapper接口
*
* @author xuyong
*
*/
public interface EmployeeDaoPlus { /**
* 分步查询测试
* 获取员工信息同时获取部门信息
* @param id
* @return
*/
Employee getEmpByIdStep(Integer id); }
编写对应的XML(sql资源文件)文件
<!-- 使用association分布查询 -->
<select id="getEmpByIdStep" resultMap="MyEmpByStep">
select * from employee 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=徐永]
业务场景二:嵌套标签collection的应用---查询部门信息时,获取该部门下所有的员工。
collection主要用于映射集合数据,工作中常见的应用场景有:
订单查询、地址查询、产品分类查询
Junit单元测试
/**
* 分步查询
* 查询部门时获取部门里所有的员工信息
*
* @throws IOException
*/
@Test
public void test10() throws IOException {
SqlSessionFactory sessionFactory = getSessionFactory();
SqlSession sqlSession = sessionFactory.openSession();
try {
DepartmentDao dao = sqlSession.getMapper(DepartmentDao.class);
Department department = dao.getDeptAndAEmpsByStep(1);
/*EmployeeDaoPlus dao2 = sqlSession.getMapper(EmployeeDaoPlus.class);
Employee employee = dao2.getEmpById(department.getEmployee().get(0).getId());*/
System.out.println(department);
} catch (Exception e) {
e.printStackTrace();
} finally {
sqlSession.close();
}
}
接口定义此处忽略
XML文件编写
<!-- collection分布查询 -->
<resultMap type="com.xuyong.entity.Department" id="DepartAndEmpByStep">
<id column="id" property="id"/>
<result column="department_name" property="departmentName"/>
<collection property="employee" select="com.xuyong.dao.EmployeeDaoPlus.getEmpByDid" column="id"></collection>
</resultMap> <!-- collection分布查询部门下的所有员工信息 -->
<select id="getDeptAndAEmpsByStep" resultMap="DepartAndEmpByStep">
select id,department_name from department
where id = #{id}
</select>
查询结果
[com.xuyong.dao.DepartmentDao.getDeptAndAEmpsByStep]-==> Preparing: select id,department_name from department where id = ?
[com.xuyong.dao.DepartmentDao.getDeptAndAEmpsByStep]-==> Parameters: 1(Integer)
[com.xuyong.dao.DepartmentDao.getDeptAndAEmpsByStep]-<== Columns: id, department_name
[com.xuyong.dao.DepartmentDao.getDeptAndAEmpsByStep]-<== Row: 1, 文案部
[com.xuyong.dao.EmployeeDaoPlus.getEmpByDid]-====> Preparing: select * from employee where d_id = ?
[com.xuyong.dao.EmployeeDaoPlus.getEmpByDid]-====> Parameters: 1(Integer)
[com.xuyong.dao.EmployeeDaoPlus.getEmpByDid]-<==== Columns: id, last_name, gender, email, d_id
[com.xuyong.dao.EmployeeDaoPlus.getEmpByDid]-<==== Row: 1, 徐永, 男, t1heluosh1@163.com, 1
[com.xuyong.dao.EmployeeDaoPlus.getEmpByDid]-<==== Row: 4, 张三, 女, 123131313, 1
[com.xuyong.dao.EmployeeDaoPlus.getEmpByDid]-<==== Total: 2
[com.xuyong.dao.DepartmentDao.getDeptAndAEmpsByStep]-<== Total: 1
Department [departmentName=文案部, employee=[Employee [dId=1, department=null, email=t1heluosh1@163.com, gender=男, id=1, lastName=徐永], Employee [dId=1, department=null, email=123131313, gender=女, id=4, lastName=张三]], id=1]
二、延迟加载
什么是延迟加载?
当我们在某项业务里需要同时获取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>
MyBatis学习——分步查询与延迟加载的更多相关文章
- MyBatis分步查询的延迟加载
延迟加载的概念只存在于分步查询时: 延迟加载的本质是为第一步查询返回的Java Bean创建了一个代理对象: 延迟加载的全局设置有两个: lazyLoadingEnabled,作用为设置select语 ...
- mybatis分步查询与延迟加载
1.分步查询 先查询用户的部门 部门Mapper.xml <resultMap id="rMap" type="com.yunqing.mybatis.bean.D ...
- Mybatis3.1-[tp_34-35]-_映射文件_select_resultMap关联查询_collection定义关联集合封装规则_collection分步查询_延迟加载
笔记要点出错分析与总结工程组织 1.定义接口 interface DepartmentMapper package com.dao; import com.bean.Department; publi ...
- Mybatis3.1-[tp_32-33]-_映射文件_select_resultMap关联查询_association分步查询_延迟加载
笔记要点出错分析与总结 工程组织 1.定义接口 DepartmentMapper package com.dao; import com.bean.Department; public interfa ...
- Mybatis学习(五)————— 延迟加载和缓存机制(一级二级缓存)
一.延迟加载 延迟加载就是懒加载,先去查询主表信息,如果用到从表的数据的话,再去查询从表的信息,也就是如果没用到从表的数据的话,就不查询从表的信息.所以这就是突出了懒这个特点.真是懒啊. Mybati ...
- mybatis学习系列二
1 参数处理(封装map过程)(23) 1.1)F5进入断点:Employee employee1=mapper.selectEmployeeByMap(map); 1.2)进入MapperProxy ...
- mybatis使用associaton进行分步查询
Employee类 public class Employee { private Integer id; private String lastName; private String email; ...
- Java框架之MyBatis 06-全局配置-mapper映射-分步查询
MyBatis MyBatis是Apache的一个开源项目iBatis, iBatis一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架. iBatis 提供的持 ...
- MyBatis学习--延迟加载
简介 在resultMap可以实现高级映射(使用association.collection实现一对一及一对多映射),association.collection具备延迟加载功能.例如:我们查询订单并 ...
随机推荐
- [视频]K8飞刀 WordPress XSS添加管理员 & GetShell 教程
[视频]K8飞刀 WordPress XSS添加管理员 & GetShell 教程 https://pan.baidu.com/s/1hq4LsmK
- IIS服务器多站点 的 https证书使用443端口 解决方案
默认情况一个服务器的IIS只能绑定一个HTTPS也就是443端口 要实现多个站点对应HTTPS只能更改IIS配置 首先把每个站点分配个不同端口,如443.444.445…(证书一定要是多域的) 然后重 ...
- selenium的chromedriver对应的chrome版本
chromedriver下载地址:http://chromedriver.storage.googleapis.com/index.html chromedriver版本 支持的Chrome版本 v2 ...
- 关于微信JS SDK接口wx.previewImage预览接口的使用
然后后之前的项目,突然往微信上迁移了,一些微信的接口没怎么用过,比较陌生,这次的功能是想调用微信的接口,实现图片放大的功能, 就找到官方文档:http://qydev.weixin.qq.com/wi ...
- k8s总结(脑图图片)
- JAVA获取运行环境的信息
System.getProperties().list(System.out); 得到运行环境的信息
- 一款Timer倒计时器
http://files.cnblogs.com/Alandre/Time.zip自己做的哦 欢迎关注
- Java-jacob-文件转HTML
Java-jacob-文件转HTML: 下载jacob的jar包,然后举个例子. public static final int WORD_HTML = 8; public static final ...
- MongoDB调优-查询优化-MongoDB Profiler
MongoDB查询优化-MongoDB Profiler MongoDB Profiler 概述 官方文档:https://docs.mongodb.com/manual/tutorial/manag ...
- 基于 CGLIB 库的动态代理机制
之前的文章我们详细的介绍了 JDK 自身的 API 所提供的一种动态代理的实现,它的实现相对而言是简单的,但是却有一个非常致命性的缺陷,就是只能为接口中的方法完成代理,而委托类自己的方法或者父类中的方 ...