一、MyBatis 延迟加载的支持
1. 延迟加载的配置
在 MyBatis 中,延迟加载可以通过全局配置和局部配置来实现:
-
全局配置:可以在 MyBatis 的配置文件中进行设置,控制全局的延迟加载行为。
<configuration> <settings> <setting name="lazyLoadingEnabled" value="true"/> <setting name="aggressiveLazyLoading" value="false"/> </settings> </configuration>
lazyLoadingEnabled
:设置为true
表示开启延迟加载,默认为false
。aggressiveLazyLoading
:设置为false
表示只有在访问某个延迟加载属性时,才会触发加载。如果设置为true
,则一旦调用了延迟加载的对象中的任意一个属性,所有延迟加载的属性都会被加载,默认为false
。
-
局部配置:可以在
ResultMap
中指定具体的映射关系是否需要延迟加载。<resultMap id="UserResultMap" type="User"> <id property="id" column="user_id"/> <result property="name" column="user_name"/> <association property="address" javaType="Address" column="address_id" select="selectAddressById" lazy="true"/> </resultMap>
lazy="true"
:表示该关联属性(如address
)采用延迟加载。
2. 延迟加载的对象关系
MyBatis 支持对一对一(association
)和一对多(collection
)的映射进行延迟加载。典型场景如下:
- 一对一:用户表和地址表之间的关系,用户的地址信息可以在需要时才加载。
- 一对多:用户和订单之间的关系,用户的订单列表可以在访问时才加载。
二、MyBatis 延迟加载的实现原理
MyBatis 的延迟加载实现基于动态代理机制(Dynamic Proxy)和 CGLIB 代理。具体来说,当 MyBatis 在处理需要延迟加载的属性时,会为这些属性生成一个代理对象,这个代理对象在被实际访问时才会触发数据库查询,加载真实的数据。
1. 动态代理机制
MyBatis 使用 Java 动态代理或 CGLIB 来实现延迟加载,主要依赖以下几个核心组件:
-
代理对象:MyBatis 为延迟加载的属性创建代理对象,这个代理对象并不包含实际的数据,而是仅仅记录下要执行的 SQL 语句及其参数。
-
Invoker 接口:这是 MyBatis 内部的一个接口,用于执行代理对象的延迟加载逻辑。它封装了 SQL 的执行和结果的返回。
-
延迟加载触发器:当代理对象的属性被访问时,触发器启动,调用
Invoker
接口,执行延迟加载的 SQL 语句,从数据库中获取数据并填充到原始对象中。
2. 实际执行过程
以下是延迟加载的实际执行过程:
-
代理对象的创建:
- 当 MyBatis 解析
ResultMap
并发现某个属性配置了lazy="true"
,它会在加载这个对象时,为该属性创建一个代理对象。这个代理对象可以是一个 Java 动态代理(基于接口的)或者是 CGLIB 代理(基于子类的)。
- 当 MyBatis 解析
-
延迟加载的触发:
- 当应用程序首次访问该代理对象的属性时,例如调用
user.getAddress()
,MyBatis 通过代理机制捕获这个访问请求。
- 当应用程序首次访问该代理对象的属性时,例如调用
-
查询执行:
- 代理对象的
Invoker
接口在捕获到访问请求后,立即执行预先定义好的 SQL 查询,从数据库中获取实际的数据。
- 代理对象的
-
结果填充:
- 查询结果返回后,MyBatis 将结果映射到目标对象(如
Address
),并填充到原始对象的相应属性中。
- 查询结果返回后,MyBatis 将结果映射到目标对象(如
-
对象替换:
- 在延迟加载完成后,MyBatis 会将代理对象替换为实际的查询结果对象,后续对该属性的访问将直接返回已经加载的数据,而不会再次触发延迟加载。
三、延迟加载的优缺点
1. 优点
-
性能优化:延迟加载可以减少不必要的数据库查询操作,尤其是在对象关联关系复杂的场景下,通过按需加载减少数据库的压力。
-
资源节省:避免一次性加载大量不必要的数据,从而节省内存和网络资源。
-
灵活性高:开发者可以根据实际需要选择性地为某些关联属性启用延迟加载,从而精细化控制数据加载行为。
2. 缺点
-
实现复杂:延迟加载的实现涉及动态代理、SQL 执行控制等机制,增加了系统的复杂性,可能导致调试困难。
-
潜在性能隐患:如果延迟加载没有妥善处理,可能会导致“n+1 查询问题”,即在加载主对象时,需要反复执行查询以加载关联对象,造成性能瓶颈。
-
开发者需注意加载时机:错误地使用延迟加载可能导致延迟加载对象未被正确加载,尤其是在 Session 生命周期管理不当的情况下。
四、延迟加载的最佳实践
-
选择性使用延迟加载:不要盲目地为所有关联关系启用延迟加载,应根据业务场景的需求,选择性地在需要优化性能的地方使用。
-
小心 n+1 查询问题:在使用一对多的延迟加载时,要特别小心 n+1 查询问题。如果可能存在大量的子对象关联查询,应考虑使用
JOIN
查询或ResultMap
配置进行一次性加载。 -
Session 生命周期管理:确保延迟加载的触发在 MyBatis
SqlSession
生命周期内进行,否则可能会引发SqlSession
关闭后无法加载数据的异常。 -
结合
aggressiveLazyLoading
进行优化:根据实际需求调整aggressiveLazyLoading
配置,在需要时提前加载关联数据,减少多次 SQL 查询的开销。 -
使用缓存:可以结合 MyBatis 的二级缓存来缓存延迟加载的结果,进一步提升性能。
五、总结
MyBatis 通过动态代理机制实现了灵活的延迟加载功能,为开发者提供了在复杂对象关系映射中优化数据库访问的手段。尽管延迟加载可以显著减少不必要的数据库查询,但也需要谨慎使用,避免潜在的性能问题。通过合理配置和良好的实践,MyBatis 的延迟加载可以在保持代码简洁性的同时,大幅度提升系统性能。
标签:对象,代理,查询,MyBatis,Mybatis,加载,延迟 From: https://blog.csdn.net/Flying_Fish_roe/article/details/143613079