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具备延迟加载功能.例如:我们查询订单并 ...
随机推荐
- firefox设置每次访问时检查缓存
1.在firefox的地址栏上输入about:config回车2.找到browser.cache.check_doc_frequency选项,双击将3改成1保存即可. 选项每个值都是什么含义的.请看下 ...
- welcome-file-list修改后不生效
用别的浏览器重新尝试一下,或者清缓存.我就是这样解决的.值得注意的就是,<welcome-file>里面指定的文件可以是.do或者是action.
- android 代码混淆示例
参考其它资料为项目代码做了一下混淆 项目中使用了 slidingmenu actionbarsherlock fastjson volley httpclient 等第三方库, 并使用了 ...
- 全网最详细的Eclipse和MyEclipse里对于Java web项目发布到Tomcat上运行成功的对比事宜【博主强烈推荐】【适合普通的还是Maven方式创建的】(图文详解)
不多说,直接上干货! 首先,大家要明确,IDEA.Eclipse和MyEclipse等编辑器之间的新建和运行手法是不一样的. 全网最详细的MyEclipse里如何正确新建普通的Java web项目并发 ...
- koa2搭建服务器
首先初始化项目 npm init -y 安装koa2 npm install koa --save 项目根目录 新建 index.js //这是最基本的服务 const Koa = require(' ...
- TCP/IP 笔记 - 传输控制协议
与UDP不同,TCP提供面向连接的.可靠的.基于字节流的传输层协议,且提供差错纠正. TCP传输的概念 对与分组丢失和比特差错的处理方法,最直接的方法是重发分组,直到它被正确接收. 这需要一种方法来判 ...
- Spring Security OAuth笔记
因为工作需要,系统权限安全方面可能要用到Spring Security OAuth2.0,所以,近几天了解了一下OAuth相关的东西.目前好像还没有系统的学习资料,学习主要是通过博客,内容都是大同小异 ...
- Lucene 7.2.1 自定义Analyzer和TokenFilter
1.自定义Analyzer: @Test public void t01() throws Exception { ArrayList<String> strings = new A ...
- Angular2入门:TypeScript的函数 - 剩余参数和箭头函数
- JavaWeb学习 (二十一)————基于Servlet+JSP+JavaBean开发模式的用户登录注册
一.Servlet+JSP+JavaBean开发模式(MVC)介绍 Servlet+JSP+JavaBean模式(MVC)适合开发复杂的web应用,在这种模式下,servlet负责处理用户请求,jsp ...