首先要了解延迟加载的概念,知道单例模式的都知道延迟加载和立即加载
- 延迟加载:只有在使用的时候才会创建并加载
- 立即加载:在初始化的时候就创建,使用的时候直接拿去用
在我们数据库的查询中,也需要有这种策略,在一对多或者多对多的关系中,我们有时候可能并不需要另一方的信息,这个时候就需要使用延迟加载,延迟加载的完成都是使用嵌套查询实现的,什么是嵌套查询,下面进行展示:
// 使用查询用户的时候查询订单来展示嵌套查询@Testpublic 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 -> createResultObjectprivate Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {this.useConstructorMappings = false; // reset previous mapping resultfinal 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 resultreturn resultObject;}// 默认为JavassistProxyFactory,configuration的getProxyFactory()protected ProxyFactory proxyFactory = new JavassistProxyFactory();public ProxyFactory getProxyFactory() {return proxyFactory;}// createProxy,说明创建出来的就是代理对象,并且代理对象是 EnhancedResultObjectProxyImplpublic 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方法@Overridepublic 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 {// 判断是否加载所有属性 || 方法是否懒加载 对应了两个配置settingif (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);}}
