首先要了解延迟加载的概念,知道单例模式的都知道延迟加载和立即加载
- 延迟加载:只有在使用的时候才会创建并加载
- 立即加载:在初始化的时候就创建,使用的时候直接拿去用
在我们数据库的查询中,也需要有这种策略,在一对多或者多对多的关系中,我们有时候可能并不需要另一方的信息,这个时候就需要使用延迟加载,延迟加载的完成都是使用嵌套查询实现的,什么是嵌套查询,下面进行展示:
// 使用查询用户的时候查询订单来展示嵌套查询
@Test
public void testNestedQuery() {
User user = userMapper.selectByIdForNestedQuery(1);
System.out.println(user);
}
// 这里只能使用 resultMap,否则无法实现嵌套查询
<resultMap id="nestedQuery" type="com.wangzhi.pojo.User">
<result column="id" property="id" javaType="int"/>
<result column="name" property="name" javaType="string"/>
<!-- 这里是关键,使用select属性 -->
<collection property="ordersList" ofType="com.wangzhi.pojo.Orders"
select="com.wangzhi.mapper.OrdersMapper.selectByUserId" column="id">
<result column="id" property="id" javaType="int"/>
<result column="name" property="name" javaType="string"/>
</collection>
</resultMap>
<select id="selectByIdForNestedQuery" resultMap="nestedQuery" parameterType="int">
SELECT id, name FROM user WHERE id = #{id}
</select>
@Select("SELECT * FROM orders WHERE user_id = #{userId} ")
List<Orders> selectByUserId(Integer userId);
测试的时候可以看到执行了两次sql,那就说明默认当前是立即加载。
实现延迟加载
局部延迟加载
局部延迟加载是针对于MappedStatement,其实就是一个属性fetchType,展示如下:
<collection property="ordersList" ofType="com.wangzhi.pojo.Orders" select="com.wangzhi.mapper.OrdersMapper.selectByUserId" column="id" fetchType="lazy">
<result column="id" property="id" javaType="int"/>
<result column="name" property="name" javaType="string"/>
</collection>
接下来在测试的时候输出对象可以先输出user.getName。可以发现查询的过程 一条sql语句。只有在用到ordersList的时候才会查询,输出另一条sql语句
全局延迟加载
全局加载就需要在配置文件中进行setting设置了。
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
优先级
针对于延迟加载来说,局部延迟加载优先于全局延迟加载,这样我们就可以再开启全局延迟加载的时候,针对某些一对一的查询开启立即加载。
延迟加载的原理
原理其实还是动态代理,当调用代理对象的延迟加载属性的getting方法的时候,也就是前面的user.getOrdersList,这里user其实已经是代理对象了,就会进入拦截器invoke方法,发现当前属性需要延迟加载时,就会发送事先保存的sql进行查询。所以说延迟加载的本质就是通过代理的形式 的,通过代理拦截到指定方法,进行数据加载。
源码分析
Mybatis的查询结果是由ResultSetHandler接⼝的handleResultSets()⽅法处理的,所以我们需要从ResultSetHandler来入手进行查看,看到实现类只有一个 DefaultResultSetHandler。
// 调用的一个方法顺序为: handleResultSets -> handleResultSet -> handleRowValues ->
// handleRowValuesForNestedResultMap -> getRowValue -> createResultObject
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
this.useConstructorMappings = false; // reset previous mapping result
final List<Class<?>> constructorArgTypes = new ArrayList<>();
final List<Object> constructorArgs = new ArrayList<>();
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) {
// 这里是重点,判断属性是否有延迟加载
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;
}
// 默认为JavassistProxyFactory,configuration的getProxyFactory()
protected ProxyFactory proxyFactory = new JavassistProxyFactory();
public ProxyFactory getProxyFactory() {
return proxyFactory;
}
// createProxy,说明创建出来的就是代理对象,并且代理对象是 EnhancedResultObjectProxyImpl
public static Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
final Class<?> type = target.getClass();
EnhancedResultObjectProxyImpl callback = new EnhancedResultObjectProxyImpl(type, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
Object enhanced = crateProxy(type, callback, constructorArgTypes, constructorArgs);
PropertyCopier.copyBeanProperties(type, target, enhanced);
return enhanced;
}
invoke方法的执行
// 执行获取属性 EnhancedResultObjectProxyImpl.invoke方法
@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;
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 {
// 判断是否加载所有属性 || 方法是否懒加载 对应了两个配置setting
if (lazyLoader.size() > 0 && !FINALIZE_METHOD.equals(methodName)) {
if (aggressive || lazyLoadTriggerMethods.contains(methodName)) {
lazyLoader.loadAll();
} else if (PropertyNamer.isSetter(methodName)) {
final String property = PropertyNamer.methodToProperty(methodName);
lazyLoader.remove(property);
} else if (PropertyNamer.isGetter(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);
}
}