1.什么是延迟加载
当加载用户信息时,不一定就要马上加载他的订单信息或者所有的好友信息,聊天记录等等,这样就需要用到延迟加载。
当我们需要用到数据的时候才进行加载,不用数据的时候就不加载,也可以叫做懒加载。
优点
先从单表查询,需要时再从关联表去关联查询,⼤⼤提⾼数据库性能,因为查询单表要比关联查询多张表速度要快。
缺点
因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗时间,所以可能造成⽤户等待时间变长,造成用户体验下降。
在多表中:
一对多,多对多:通常情况下采用延迟加载
一对一(多对一):通常情况下采用立即加载注意:
延迟加载是基于(嵌套查询)来实现的
2.实现
局部延迟加载
在association和collection标签中都有⼀个fetchType属性,通过修改它的值,可以修改局部的加载策略。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--命名空间方便隔离-->
<mapper namespace="com.lazyload.mapper.IAccountMapper">
<!--将Account的信息和User的信息封装到ResultMap中去-->
<!-- property表示java实体类中属性的名称,javaType表示属性的类型,ofType表示泛型,column表示应用查询的某列 select表示需要执行的sql语句 fetchType表示是否开启延迟加载eager取消延迟加载,lazy开启延迟加载,默认开启 -->
<resultMap id="userAccountMap" type="com.lazyload.entites.Account">
<id property="id" column="id"></id>
<result property="uid" column="uid"></result>
<result property="money" column="money"></result>
<!--一对一的映射关系:配置封装的内容
select属性指定的内容:查询用户的唯一标识【com.itheima.dao.IAccountMapper+方法名(就是接口中的)】
column属性指定的内容:根据id查询时,所需要的参数的值(根据id来查询数据库的)
-->
<association property="user" column="uid" javaType="com.lazyload.entites.User" select="findAccountByUid" feetchType="lazy"></association>
</resultMap>
<!--封装的查询所有-->
<select id="findAllAccount" resultMap="userAccountMap">
select * from account
</select>
<!--根据用户id查询账户列表-->
<select id="findAccountByUid" parameterType="integer" resultType="com.lazyload.entites.Account">
select * from account where uid=#{uid}
</select>
</mapper>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--命名空间方便隔离-->
<mapper namespace="com.lazyload.mapper.IUserMapper">
<!--写入user的resultMap:将user和数据库对应
作用:建立SQL查询结果字段与实体属性的映射关系
-->
<resultMap id="userAccountMap" type="com.lazyload.entites.User">
<!--主键字段-->
<!--column所表示的是数据库中的字段名称-->
<id property="id" column="id"></id>
<!--非主键字段-->
<result property="username" column="username"></result>
<result property="birthday" column="birthday"></result>
<result property="sex" column="sex"></result>
<result property="address" column="address"></result>
<!--Userx下对应的对应Account账户的信息:配置user对象下account的映射-->
<collection property="accounts" select="findUserById" column="id"></collection>
</resultMap>
<!--查询所有-->
<select id="findAll" resultMap="userAccountMap">
select * from user
</select>
<!--查询一个通过id-->
<select id="findUserById" parameterType="int" resultType="com.lazyload.entites.User">
select * from user where id=#{id}
</select>
</mapper>
参考链接:https://blog.csdn.net/friggly/article/details/124686876?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_utm_term~default-0-124686876-blog-108432460.235^v38^pc_relevant_default_base&spm=1001.2101.3001.4242.1&utm_relevant_index=3
当查询用户基本信息时,只返回用户基本信息,而不进行其余账号信息的查询,当触发关键方法的时候才进行查询,具体的进入参考链接学习讲的非常好,到位学习链接。
全局延迟加载
#全局启用延迟加载
mybatis.configuration.lazy-loading-enable=true
#为false时,所有关联对象都会按需求加载
mybatis.configuration.aggressive-lazy-loading=false
#设置方法触发加载,不设置时默认为下面这几个方法触发
mybatis.configuration.lazy-load-trigger-methods=equals,clone,hashCode
局部加载策略优先级高于全局加载策略,同时在xml映射文件里修改feetchType属性为eager来取消延迟加载。
3、延迟加载原理实现
它的原理是,使用 CGLIB 或 Javassist( 默认 ) 创建目标对象的代理对象。当调用代理对象的延迟加载属性的 getting 方法时,进入拦截器方法。比如调⽤ a.getB().getName() 方法,进入拦截器的invoke(...) 方法,发现 a.getB() 需要延迟加载时,那么就会单独发送事先保存好的查询关联 B对象的 SQL ,把 B 查询上来,然后调用a.setB(b) 方法,于是 a 对象 b 属性就有值了,接着完成a.getB().getName() 方法的调用。这就是延迟加载的基本原理。
总结:延迟加载主要是通过动态代理的形式实现,通过代理拦截到指定方法,执行数据加载。