@[TOC]
聊聊Mybatis的延迟加载
延迟加载就是在需要的数据的时候再进行加载,也就是懒加载,延迟加载是基于嵌套查询来实现的,一般在一对多,多对多的时候使用延迟加载,一对一或者多对一的时候使用立即加载
全局延迟加载
全局延迟加载:
在settings标签下配置lazyLoadingEnabled属性:
<settings> <setting name="lazyLoadingEnabled" value="true"/> </settings>lazyLoadingEnabled,当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。
局部延迟加载
局部延迟加载:
association和collection标签中都有fetchType属性,有效值为 lazy 和 eager。 指定属性后,将在映射中忽略全局配置参数 lazyLoadingEnabled
<resultMap id="orderMap" type="order"> <collection property="orderList" ofType="order" column="id" select="findByUid" fetchType="lazy"> </collection> </resultMap>具体延迟加载逻辑
延迟加载入口代码是结果集的映射,ResultSetHandler的handleResultSets,ResultSetHandler默认实现类是DefaultResultSetHandler,最后调用它的getRowValue()方法得到结果值,这个方法内的第一步就是根据ResultMap的type属性创建对应的结果对象
Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException { ... Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix); if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) { final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings(); for (ResultMapping propertyMapping : propertyMappings) { // issue gcode #109 && issue #149 if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) { resultObject = configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs); break; } } } this.useConstructorMappings = resultObject != null && !constructorArgTypes.isEmpty(); // set current mapping result return resultObject; }关键代码判断属性有没有嵌套查询,如果有的话就创建代理对象:configuration.getProxyFactory().createProxy(),调用的是ProxyFactory接口,它有两个实现类:JavassistProxyFactory和 CglibProxyFactory,默认是JavassistProxyFactory,调用createProxy()中通过调用内部类EnhancedResultObjectProxyImpl的createProxy()方法从而创建出代理对象
EnhancedResultObjectProxyImpl实现了MethodHandler接口,它重写invoke()方法进行拦截,在invoke()方法中有这么一段代码:
if (PropertyNamer.isGetter(methodName)) { final String property = PropertyNamer.methodToProperty(methodName); if (lazyLoader.hasLoader(property)) { lazyLoader.load(property); }也就是如果启用了延迟加载并且是getter方法的话,就执行load加载方法,否则执行原方法
ResultLoaderMap维护延迟加载的属性和ResultLoader的映射关系,它的loadMap属性是hashmap,value 是LoadPair,LoadPair中有ResultLoader属性, ResultLoader记录了一次延迟加载涉及的所有的信息,调用LoadPair的load方法中再执行一次sql,并将结果通过setValue()方法设置
总结
这篇文章我们讲了全局延迟加载的配置和局部延迟加载的配置,并介绍了延迟加载的大体过程:使用JavassistProxyFactory创建代理对象,当调用代理对象的延迟加载属性的getter方法的时候,进入invoke()方法进行拦截,如果属性需要延迟加载,就发送之前保存好的sql,执行sql把结果设置到类的属性中。