Mybatis查询延迟加载详解及实例
发布时间 - 2026-01-10 22:16:16 点击率:次Mybatis查询延迟加载详解及实例

1.1 启用延迟加载
Mybatis的延迟加载是针对嵌套查询而言的,是指在进行查询的时候先只查询最外层的SQL,对于内层SQL将在需要使用的时候才查询出来。Mybatis的延迟加载默认是关闭的,即默认是一次就将所有的嵌套SQL一并查了将对象所有的信息都查询出来。开启延迟加载有两种方式。
第一种是在对应的<collection>或<association>标签上指定fetchType属性值为“lazy”。如下示例中我们在查询id为selectByPrimaryKey的查询时会返回BaseResultMap,在BaseResultMap中,我们指定了属性“nodes”是一个集合类型的,而且是需要通过id为selectNodes的查询进行查询的,我们指定了该查询的fetchType为lazy,即延迟加载。
<resultMap id="BaseResultMap" type="com.elim.learn.mybatis.model.SysWfProcess">
<id column="id" jdbcType="INTEGER" property="id" />
<result column="template_id" jdbcType="INTEGER" property="templateId" />
<result column="creator" jdbcType="INTEGER" property="creator" />
<result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
<collection property="nodes" column="id"
ofType="com.elim.learn.mybatis.model.SysWfNode" select="selectNodes" fetchType="lazy"/>
</resultMap>
<resultMap id="SysWfNodeResult" type="com.elim.learn.mybatis.model.SysWfNode">
<id column="id" jdbcType="INTEGER" property="nodeId" />
<result column="process_id" jdbcType="INTEGER" property="processId" />
<result column="node_code" jdbcType="VARCHAR" property="nodeCode" />
<result column="node_name" jdbcType="VARCHAR" property="nodeName" />
</resultMap>
<select id="selectByPrimaryKey" parameterType="java.lang.Integer"
resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from sys_wf_process
where id = #{id,jdbcType=INTEGER}
</select>
<select id="selectNodes"
resultMap="SysWfNodeResult">
select id, process_id, node_code, node_name from sys_wf_node
where process_id=#{id}
</select>
第二种是开启全局的延迟加载。通过在Mybatis的配置文件的<settings>标签下加上如下配置可开启全局的延迟加载。开启了全局的延迟加载后我们就无需再在各个嵌套的子查询上配置延迟加载了,如果有某一个嵌套的子查询是不需要延迟加载的,可以设置其fetchType=”eager”。设置在嵌套查询上的fetchType可以覆盖全局的延迟加载设置。
<setting name="lazyLoadingEnabled" value="true"/>
1.2 分析
Mybatis的查询结果是由ResultSetHandler接口的handleResultSets()方法处理的。ResultSetHandler接口只有一个实现,DefaultResultSetHandler。有兴趣的朋友可以去看一下它的源码,看一下它是如何处理结果集的。对于本文的主题,延迟加载相关的一个核心的方法就是如下这个创建返回结果对象的方法。
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
final List<Class<?>> constructorArgTypes = new ArrayList<Class<?>>();
final List<Object> constructorArgs = new ArrayList<Object>();
final Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
if (resultObject != null && !typeHandlerRegistry.hasTypeHandler(resultMap.getType())) {
final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
for (ResultMapping propertyMapping : propertyMappings) {
// issue gcode #109 && issue #149
if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) {
return configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
}
}
}
return resultObject;
}
在上面方法中我们可以看到Mybatis先是根据正常情况创建一个返回类型对应的对象。当我们的ResultMap是包含子查询的时候,其会在我们正常返回类型对象的基础上创建对应的代理对象。对,你没有看错,就是我们的直接结果是代理对象,而不是子查询对应的属性是代理对象。默认是基于JavassistProxyFactory类创建的代理对象。可以通过Mybatis的全局配置proxyFactory来更改,可选值是CGLIB和JAVASSIST,默认是后者。需要使用CGLIB代理时注意加入CGLIB的包。
<setting name="proxyFactory" value="CGLIB"/>
回过头来看我们之前的那个延迟加载的配置,我们的一个查询返回的是SysWfProcess类型的对象,其有一个SysWfNode集合类型的nodes属性,nodes属性是通过一个子查询查出来的,而且是延迟加载。这个时候我们来进行以下测试。
@Test
public void testLazyLoad1() {
SysWfProcessMapper mapper = this.session.getMapper(SysWfProcessMapper.class);
SysWfProcess process = mapper.selectByPrimaryKey(1);
System.out.println(process.getClass());
}
这个时候你会发现,上面的测试代码的输出结果是一个代理类,而不是我们自己的com.elim.learn.mybatis.model.SysWfProcess类型。另外如果你启用了日志输出,并且是打印的DEBUG日志,你会看到Mybatis是发了两条SQL进行查询的。
2016-12-23 15:43:21,131 DEBUG [main] (BaseJdbcLogger.java:145) - ==> Preparing: select id, template_id, creator, create_time from sys_wf_process where id = ? 2016-12-23 15:43:21,156 DEBUG [main] (BaseJdbcLogger.java:145) - ==> Parameters: 1(Integer) 2016-12-23 15:43:21,269 DEBUG [main] (BaseJdbcLogger.java:145) - <== Total: 1 class com.elim.learn.mybatis.model.SysWfProcess_$$_jvstc25_0 2016-12-23 15:43:21,271 DEBUG [main] (BaseJdbcLogger.java:145) - ==> Preparing: select id, process_id, node_code, node_name from sys_wf_node where process_id=? 2016-12-23 15:43:21,272 DEBUG [main] (BaseJdbcLogger.java:145) - ==> Parameters: 1(Integer) 2016-12-23 15:43:21,274 DEBUG [main] (BaseJdbcLogger.java:145) - <== Total: 2
但是如果我们把最后一个System.out.println()去掉,也就是说我们只是从数据库中查询出SysWfProcess对象,而不使用它的时候,通过查看日志输出你会发现Mybatis又只会发送一条SQL,即只是查询出SysWfProcess的信息。这是为什么呢?
1.3 aggressiveLazyLoading
这是因为当我们启用了延迟加载时,我们的查询结果返回的是一个代理对象,当我们访问该代理对象的方法时,都会触发加载所有的延迟加载的对象信息。这也就可以很好的解释上面的场景。但是如果是这样的设计,貌似Mybatis的延迟加载作用不大。但事实并非如此,这只是Mybatis的一个默认策略,我们可以通过Mybatis的全局配置aggressiveLazyLoading来改变它,默认是true,表示延迟加载时将在第一次访问代理对象的方法时就将全部的延迟加载对象加载出来。当设置为false时则会在我们第一次访问延迟加载的对象的时候才会从数据库加载对应的数据。注意在延迟对象未从数据库加载出来前,我们对应延迟对象的属性将是null,因为你没有对它赋值。
<setting name="aggressiveLazyLoading" value="fasle"/>
1.4 lazyLoadTriggerMethods
那如果我们设置了aggressiveLazyLoading=”false”,但又希望在调用某些方法之前把所有的延迟对象都从数据库加载出来,怎么办呢?这个时候我们可以通过lazyLoadTriggerMethods参数来指定需要加载延迟对象的方法调用。默认是equals、clone、hashCode和toString,也就是说我们在调用代理对象的这些方法之前就会把延迟加载对象从数据库加载出来。
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString" />
Mybatis延迟加载生成的代理对象的代理过程,可以参考ProxyFactory的创建代理对象的过程,以下是基于Javassist创建的代理对象的代理过程,基于CGLIB的代理也是类似的。从下面的代码我们可以看到Mybatis的代理对象需要从数据库加载延迟对象时是在目标方法被调用以前发生的,这就可以保证我们的目标方法被调用时延迟加载的对象已经从数据库中加载出来了。
@Override
public Object invoke(Object enhanced, Method method, Method methodProxy, Object[] args) throws Throwable {
final String methodName = method.getName();
try {
synchronized (lazyLoader) {
if (WRITE_REPLACE_METHOD.equals(methodName)) {
Object original = null;
if (constructorArgTypes.isEmpty()) {
original = objectFactory.create(type);
} else {
original = objectFactory.create(type, constructorArgTypes, constructorArgs);
}
PropertyCopier.copyBeanProperties(type, enhanced, original);
if (lazyLoader.size() > 0) {
return new JavassistSerialStateHolder(original, lazyLoader.getProperties(), objectFactory, constructorArgTypes, constructorArgs);
} else {
return original;
}
} else {
if (lazyLoader.size() > 0 && !FINALIZE_METHOD.equals(methodName)) {
if (aggressive || lazyLoadTriggerMethods.contains(methodName)) {
lazyLoader.loadAll();
} else if (PropertyNamer.isProperty(methodName)) {
final String property = PropertyNamer.methodToProperty(methodName);
if (lazyLoader.hasLoader(property)) {
lazyLoader.load(property);
}
}
}
}
}
return methodProxy.invoke(enhanced, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
}
本文是介绍的都是基于<collection>这种关联,其实<association>关联的对象的延迟加载也是一样的,它们的默认策略也是一样的。
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!
# Mybatis查询延迟加载
# Mybatis查询延迟加载分析
# Mybatis查询延迟加载实例分析
# Mybatis一对一延迟加载实现过程解析
# Mybatis一对多延迟加载实现代码解析
# Mybatis如何实现延迟加载及缓存
# Mybatis延迟加载的实现方式
# Mybatis延迟加载和缓存深入讲解
# mybatis 延迟加载的深入理解
# mybatis中延迟加载Lazy策略的方法
# MyBatis 延迟加载、一级缓存、二级缓存(详解)
# mybatis教程之延迟加载详解
# Mybatis中的延迟加载案例解析
# 解析Mybatis延迟加载问题
# 加载
# 这个时候
# 的是
# 当我们
# 是一个
# 是在
# 会在
# 我们可以
# 可以看到
# 你会发现
# 数据库中
# 查询结果
# 自己的
# 而不是
# 都是
# 这是
# 也就是说
# 就会
# 如果你
# 很好
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
Laravel如何安装Breeze扩展包_Laravel用户注册登录功能快速实现【流程】
Laravel怎么使用Session存储数据_Laravel会话管理与自定义驱动配置【详解】
Midjourney怎样加参数调细节_Midjourney参数调整技巧【指南】
Microsoft Edge如何解决网页加载问题 Edge浏览器加载问题修复
php做exe能调用系统命令吗_执行cmd指令实现方式【详解】
Laravel如何实现用户注册和登录?(Auth脚手架指南)
如何确保FTP站点访问权限与数据传输安全?
Bootstrap整体框架之JavaScript插件架构
mc皮肤壁纸制作器,苹果平板怎么设置自己想要的壁纸我的世界?
历史网站制作软件,华为如何找回被删除的网站?
iOS中将个别页面强制横屏其他页面竖屏
如何批量查询域名的建站时间记录?
Laravel如何处理和验证JSON类型的数据库字段
中国移动官方网站首页入口 中国移动官网网页登录
Laravel路由怎么定义_Laravel核心路由系统完全入门指南
canvas 画布在主流浏览器中的尺寸限制详细介绍
如何用景安虚拟主机手机版绑定域名建站?
高性能网站服务器配置指南:安全稳定与高效建站核心方案
小米17系列还有一款新机?主打6.9英寸大直屏和旗舰级影像
Laravel 419 page expired怎么解决_Laravel CSRF令牌过期处理
百度输入法ai面板怎么关 百度输入法ai面板隐藏技巧
移动端脚本框架Hammer.js
Laravel怎么实现模型属性转换Casting_Laravel自动将JSON字段转为数组【技巧】
装修招标网站设计制作流程,装修招标流程?
Laravel如何发送邮件和通知_Laravel邮件与通知系统发送步骤
HTML5建模怎么导出为FBX格式_FBX格式兼容性及导出步骤【指南】
Windows10如何更改计算机工作组_Win10系统属性修改Workgroup
QQ浏览器网页版登录入口 个人中心在线进入
企业在线网站设计制作流程,想建设一个属于自己的企业网站,该如何去做?
北京企业网站设计制作公司,北京铁路集团官方网站?
广州网站制作公司哪家好一点,广州欧莱雅百库网络科技有限公司官网?
在线教育网站制作平台,山西立德教育官网?
Laravel Seeder怎么填充数据_Laravel数据库填充器的使用方法与技巧
如何为不同团队 ID 动态生成多个“认领值班”按钮
logo在线制作免费网站在线制作好吗,DW网页制作时,如何在网页标题前加上logo?
Laravel如何生成API文档?(Swagger/OpenAPI教程)
Laravel如何处理CORS跨域问题_Laravel项目CORS配置与解决方案
Laravel怎么实现API接口鉴权_Laravel Sanctum令牌生成与请求验证【教程】
如何快速搭建安全的FTP站点?
微信小程序 canvas开发实例及注意事项
今日头条AI怎样推荐抢票工具_今日头条AI抢票工具推荐算法与筛选【技巧】
阿里云高弹*务器配置方案|支持分布式架构与多节点部署
利用JavaScript实现拖拽改变元素大小
如何确认建站备案号应放置的具体位置?
网站广告牌制作方法,街上的广告牌,横幅,用PS还是其他软件做的?
如何在Windows服务器上快速搭建网站?
如何在阿里云ECS服务器部署织梦CMS网站?
品牌网站制作公司有哪些,买正品品牌一般去哪个网站买?
夸克浏览器网页跳转延迟怎么办 夸克浏览器跳转优化
在线制作视频网站免费,都有哪些好的动漫网站?

