MyBatis 源码学习14——级联映射及懒加载

MyBatis级联映射及懒加载

MyBatis Mapper配置中通过<association>标签建立一对一映射,通过<collection>标签建立一对多映射。

<association>和<collection>标签有两种映射方式:

一种是为Java实体属性关联一个外部的查询Mapper,MyBatis实际上为实体的属性执行一次额外的查询操作;

一种是为实体属性的每个字段配置映射,然后通过JOIN语句进行关联查询。

除此还有一种额外标签Discriminator:类似于Java中的switch语法,能够根据数据库记录中某个字段的值映射到不同的ResultMap

懒加载机制:上面映射的第一种情况,当需要查看关联的信息时,才执行一次额外的查询操作,在一定程度上能减少数据库IO次数,提升系统性能。

MyBatis主配置文件中提供了lazyLoadingEnabled和aggressiveLazyLoading参数用来控制是否开启懒加载机制。
MyBatis 源码学习14——级联映射及懒加载

lazyLoadingEnabled:true时表示开启懒加载,否则表示不开启懒加载。
aggressiveLazyLoading:控制ResultMap默认的加载行为,参数值为false表示ResultMap默认的加载行为为懒加载,否则为积极加载。

<collection><association>标签还提供了一个fetchType属性,用于控制级联查询的加载行为,fetchType属性值为lazy时表示该级联查询采用懒加载方式,当fetchType属性值为eager时表示该级联查询采用积极加载方式。

当我们开启懒加载时,执行查询Mapper返回的实际上是通过Cglib或Javassist创建的动态代理对象。

假设我们指定了使用Cglig创建动态代理对象,调用动态代理对象的Getter方法时会执行MyBatis中定义的拦截逻辑:
MyBatis 源码学习14——级联映射及懒加载

EnhancedResultObjectProxyImpl是CglibProxyFactory类中的一个内部类,EnhancedResultObjectProxyImpl的intercept()方法中定义了调用动态代理对象方法的拦截逻辑。

当我们调用代理实体对象的Getter方法获取属性时,会执行EnhancedResultObjectProxyImpl类的intercept()方法中的拦截逻辑。

在EnhancedResultObjectProxyImpl类的intercept()方法中,获取Getter方法对应的属性名称,然后调用ResultLoaderMap对象的hasLoader()方法判断该属性是否是懒加载属性,如果是,则调用ResultLoaderMap对象的load()方法加载该属性,ResultLoaderMap对象的load()方法最终会调用LoadPair对象的load()方法
MyBatis 源码学习14——级联映射及懒加载

1.创建了一个ResultLoader对象,

2.然后ResultLoader对象的loadResult()方法执行查询操作,将查询结果赋值给对应的Java实体属性。
MyBatis 源码学习14——级联映射及懒加载

ResultLoader类的loadResult()方法:调用selectList()方法完成查询操作,selectList()方法中最终会调用Executor对象的query()方法完成查询操作。

MyBatis懒加载机制的实现原理总结:通过动态代理实现的,当开启懒加载配置时,调用Mapper查询的结果是通过Cglib或Javassist创建的代理对象,当调用代理对象的Getter方法获取属性值时,会执行动态代理的拦截方法,在拦截方法中,通过Getter方法名称获取Java实体属性名称,
然后根据属性名称获取对应的LoadPair对象,LoadPair对象中维护了Mapper的Id,有了Mapper的Id就可以获取对应的MappedStatement对象,接着执行一次额外的查询操作,使用查询结果为懒加载属性赋值。