如果有遗漏,评论区告诉我进行补充
面试官: Mybatis是否支持延迟加载?如果支持,它的实现原理是什么?
我回答:
MyBatis中的延迟加载
一、延迟加载的配置
全局配置:
在MyBatis的配置文件mybatis-config.xml中,可以通过 <settings> 标签来设置全局的延迟加载行为。
lazyLoadingEnabled: 设置为true表示开启延迟加载,默认值为false。aggressiveLazyLoading: 设置为false表示只有在访问某个延迟加载属性时,才会触发加载。如果设置为true,则一旦调用了延迟加载的对象中的任意一个属性,所有延迟加载的属性都会被加载。默认情况下,该值为false。
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
局部配置:
可以在 <association> 或 <collection> 标签中通过 fetchType="lazy" 来指定具体的映射关系是否需要延迟加载。
<resultMap id="personResultMap" type="Person">
<id property="id" column="id"/>
<result property="name" column="name"/>
<association property="address" javaType="Address"
select="selectAddressById" column="address_id" fetchType="lazy"/>
</resultMap>
二、延迟加载的实现原理
MyBatis使用动态代理机制(Dynamic Proxy)和CGLIB代理来实现延迟加载功能。
-
代理对象的创建:
- 当MyBatis解析
<resultMap>并发现某个属性配置了fetchType="lazy"时,它会在加载这个对象时,为该属性创建一个代理对象。 - 这个代理对象可以是一个Java动态代理(基于接口的)或者是CGLIB代理(基于子类的)。代理对象并不包含实际的数据,而是记录下要执行的SQL语句及其参数。
- 当MyBatis解析
-
延迟加载的触发:
- 当应用程序首次访问该代理对象的属性时(例如调用
user.getAddress()),MyBatis通过代理机制捕获这个访问请求。 - 代理对象的Invoker接口在捕获到访问请求后,立即执行预先定义好的SQL查询,从数据库中获取实际的数据。
- 当应用程序首次访问该代理对象的属性时(例如调用
-
查询结果的返回与缓存:
- 查询结果返回后,MyBatis将结果映射到目标对象(如
Address),并填充到原始对象的相应属性中。 - 在延迟加载完成后,MyBatis会将代理对象替换为实际的查询结果对象。后续对该属性的访问将直接返回已经加载的数据,而不会再次触发延迟加载。
- 查询结果返回后,MyBatis将结果映射到目标对象(如
三、延迟加载的性能优化
-
减少不必要的数据库查询操作:
- 延迟加载可以显著减少不必要的数据库查询操作,尤其是在对象关联关系复杂的场景下。
-
Session生命周期管理:
- 确保延迟加载的触发在MyBatis SqlSession生命周期内进行,否则可能会引发SqlSession关闭后无法加载数据的异常。
-
结合
aggressiveLazyLoading进行优化:- 根据实际需求调整
aggressiveLazyLoading配置,在需要时提前加载关联数据,减少多次SQL查询的开销。
- 根据实际需求调整
-
使用缓存:
- 可以结合MyBatis的二级缓存来缓存延迟加载的结果,进一步提升性能。
注意事项
尽管延迟加载可以显著减少不必要的数据库查询,但也需要注意以下几点:
-
N+1查询问题:虽然延迟加载可以避免一次性加载大量关联数据,但如果设计不当,可能会导致N+1查询问题。因此,在使用延迟加载时,应仔细评估应用的具体需求和性能影响。
-
事务管理:由于延迟加载会在后续时间点执行查询,这要求原事务仍然有效。否则,可能无法正确地加载关联数据。
综上所述,MyBatis通过动态代理机制实现了灵活的延迟加载功能,为开发者提供了在复杂对象关系映射中优化数据库访问的手段。理解其工作原理有助于更好地利用这一特性,同时避免潜在的问题。通过合理配置和良好的实践,MyBatis的延迟加载可以在保持代码简洁性的同时,大幅度提升系统性能。

139

被折叠的 条评论
为什么被折叠?



