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具备延迟加载功能.例如:我们查询订单并 ...
随机推荐
- TestNG依赖测试
某些复杂的测试场景需要按照特定的顺序执行测试用例,以保证某个测试用例被执行之后才执行其测试用例,此测试场景运行需求称为依赖测试.TestNG支持依赖测试,使用dependsOnMethods参数来实现 ...
- 高可用Hadoop平台-应用JAR部署
1.概述 今天在观察集群时,发现NN节点的负载过高,虽然对NN节点的资源进行了调整,同时对NN节点上的应用程序进行重新打包调整,负载问题暂时得到缓解.但是,我想了想,这样也不是长久之计.通过这个问题, ...
- apache log4j打印日志源码出口
Throwable.class: public void printStackTrace(PrintStream s) { synchronized (s) { s.println(this); St ...
- kong+konga
kong+kongau 注意kong1.0与之前版本API发生了很大的改动,所以是不兼容的,kongav0.14.0是匹配kong1.0+的,不兼容1.0之前的.可以根据自己的需求进行选择. kong ...
- 由sql注入联想到PreparedStatement
PreparedStatement极大地提高了安全性. 即使到目前为止,仍有一些人连基本的恶意SQL语法都不知道. String sql = "select * from tb_ ...
- 【学习笔记】JS经典异步操作,从闭包到async/await
参考文献:王仕军——知乎专栏前端周刊 感谢作者的热心总结,本文在理解的基础上,根据自己能力水平作了一点小小的修改,在加深自己印象的同时也希望能和各位共同进步... 1. 异步与for循环 抛出一个问题 ...
- MySQL中的事务及读写锁实现并发访问控制
一.并发控制中锁的概念 锁是并发控制中最核心的概念之一,在MySQL中的锁分两大类,一种是读锁,一种是写锁,读锁也可以称为共享锁(shared lock),写锁也通常称为排它锁(exclusive l ...
- 数据分析之numpy模块
numpy(numerical python)是python语言的一个扩展程序库,支持大量的维度数组和矩阵运算,此外也针对数组提供大量的数学函数库. 一.创建数组 1 使用array()创建 impo ...
- VirtualBox centos7扩容
有时候扩容还真不如重新建立一个大硬盘的系统,但是如果你安装了好多东西的话,那还是来扩容一下吧. 查看磁盘格式 在virtualBox中右键点击虚拟机->设置->存储,如 ...
- [译]WebAPI下的如何实现参数绑定
本文将概述在WebAPI方式下将如何将参数绑定到一个action方法,包括参数是如何被读取,一系列规则决定特定环境采用的那种绑定方式,文章最后将给出一些实际的例子. Parameter binding ...