声明:面试是遇到延迟加载问题,在网页搜索到此篇文章,感觉很有帮助,留此学习之用!

一、分步查询

分步查询通常应用于关联表查询,如:电商平台,查询订单信息时需要查询部分的用户信息;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学习——分步查询与延迟加载的更多相关文章

  1. MyBatis分步查询的延迟加载

    延迟加载的概念只存在于分步查询时: 延迟加载的本质是为第一步查询返回的Java Bean创建了一个代理对象: 延迟加载的全局设置有两个: lazyLoadingEnabled,作用为设置select语 ...

  2. mybatis分步查询与延迟加载

    1.分步查询 先查询用户的部门 部门Mapper.xml <resultMap id="rMap" type="com.yunqing.mybatis.bean.D ...

  3. Mybatis3.1-[tp_34-35]-_映射文件_select_resultMap关联查询_collection定义关联集合封装规则_collection分步查询_延迟加载

    笔记要点出错分析与总结工程组织 1.定义接口 interface DepartmentMapper package com.dao; import com.bean.Department; publi ...

  4. Mybatis3.1-[tp_32-33]-_映射文件_select_resultMap关联查询_association分步查询_延迟加载

    笔记要点出错分析与总结 工程组织 1.定义接口 DepartmentMapper package com.dao; import com.bean.Department; public interfa ...

  5. Mybatis学习(五)————— 延迟加载和缓存机制(一级二级缓存)

    一.延迟加载 延迟加载就是懒加载,先去查询主表信息,如果用到从表的数据的话,再去查询从表的信息,也就是如果没用到从表的数据的话,就不查询从表的信息.所以这就是突出了懒这个特点.真是懒啊. Mybati ...

  6. mybatis学习系列二

    1 参数处理(封装map过程)(23) 1.1)F5进入断点:Employee employee1=mapper.selectEmployeeByMap(map); 1.2)进入MapperProxy ...

  7. mybatis使用associaton进行分步查询

    Employee类 public class Employee { private Integer id; private String lastName; private String email; ...

  8. Java框架之MyBatis 06-全局配置-mapper映射-分步查询

    MyBatis MyBatis是Apache的一个开源项目iBatis, iBatis一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架. iBatis  提供的持 ...

  9. MyBatis学习--延迟加载

    简介 在resultMap可以实现高级映射(使用association.collection实现一对一及一对多映射),association.collection具备延迟加载功能.例如:我们查询订单并 ...

随机推荐

  1. 为什么我们喜欢用 sigmoid 这类 S 型非线性变换?

    本文整理自 @老师木 的一条图片新浪微博,从另一个角度给出为何采用 sigmoid 函数作非线性变换的解释. 为什么我们喜欢用 sigmoid 这类 S 型非线性变换?

  2. ubuntu 增加一个用户 并赋予权限

    一.添加一个用户 sudo adduser tommy  //添加一个tommyd的用户 sudo passwd tommy // 修改密码 回车后出现一下提示 输入密码即可Changing pass ...

  3. 从零开始学 Web 之 JavaScript(二)变量

    大家好,这里是「 Daotin的梦呓 」从零开始学 Web 系列教程.此文首发于「 Daotin的梦呓 」公众号,欢迎大家订阅关注.在这里我会从 Web 前端零基础开始,一步步学习 Web 相关的知识 ...

  4. 使用crypt配置Basic Auth登录认证

    简介 Basic Auth用于服务端简单的登录认证,通常使用服务器Nginx.Apache本身即可完成.比如我们要限定某个域名或者页面必须输入用户名.密码才能登录,但又不想使用后端开发语言,此时Bas ...

  5. 网络基础知识-TCP/IP协议各层详解

    TCP/IP简介 虽然大家现在对互联网很熟悉,但是计算机网络的出现比互联网要早很多. 计算机为了联网,就必须规定通信协议,早期的计算机网络,都是由各厂商自己规定一套协议,IBM.Apple和Micro ...

  6. C++ 输入、输出运算符重载

    C++ 能够使用流提取运算符 >> 和流插入运算符 << 来输入和输出内置的数据类型.我们可以重载流提取运算符和流插入运算符来操作对象等用户自定义的数据类型. 在这里,有一点很 ...

  7. SQL 同一张表中相同字段的内容合并为一条记录(不同字段的那一列每个记录后面加逗号)

    一.创建表 create table stuUnion ( sid int identity primary key, cid int, id ) ) 二.添加数据 insert into stuUn ...

  8. C# 中的集合(Array/ArrayList/List<T>/HashTable/Dictionary)

    int [] numbers = new int[5]; // 长度为5,元素类型为 int.string[,] names = new string[5,4]; // 5*4 的二维数组byte[] ...

  9. 转载 一位资深程序员大牛给予Java初学者的学习路线建议

    原文链接:http://geek.csdn.net/news/detail/242336 Java学习这一部分其实也算是今天的重点,这一部分用来回答很多群里的朋友所问过的问题,那就是你是如何学习Jav ...

  10. hadoop之 hadoop能为企业做什么?

    hadoop是什么? Hadoop是一个开源的框架,可编写和运行分不是应用处理大规模数据,是专为离线和大规模数据分析而设计的,并不适合那种对几个记录随机读写的在线事务处理模式.Hadoop=HDFS( ...