我是靠谱客的博主 哭泣鞋子,最近开发中收集的这篇文章主要介绍MyBatis 延迟加载,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

延迟加载:也叫做懒加载,就是用到数据时才进行查询加载,不需要用到时不进行加载。
优缺点:

  • 优点:先从单表查询,需要时再从关联表去关联查询,⼤⼤提⾼数据库性能,因为查询单表要⽐关联查询多张表速度要快.
  • 缺点:因为只有在用到数据时,才会去查询数据库,这样在大批量的数据查询时,查询工作也会消耗时间,可能造成用户请求响应慢,造成用户等待时间边长,使用户体验感变差。

适用场景:

  • 延迟加载:在一对多、多对多时采用延迟加载;
  • 立即加载:在一对一、多对一时采用立即加载。

延迟加载是基于嵌套查询来实现的。

局部加载的实现方式:
通过修改 association 和 collection 标签中的 fetchType 属性,可以控制局部加载策略。
开启一对多延迟加载:
fetchType=“lazy” 懒加载策略
fetchType=“eager” ⽴即加载策略

<resultMap id="userMap" type="User">
        <result property="id" column="id"></result>
        <result property="userName" column="username"></result>
        <result property="passWord" column="passWord"></result>
        <result property="birthday" column="birthday"></result>
        <collection property="orders" ofType="com.xxx.pojo.Order" fetchType="lazy">
            <result property="id" column="oid"></result>
            <result property="orderTime" column="orderTime"></result>
            <result property="total" column="total"></result>
        </collection>
    </resultMap>

全局加载实现方式:
在Mybatis的核⼼配置⽂件中可以使⽤setting标签修改全局的加载策略。

<settings>
	<!--开启全局延迟加载功能-->
	<setting name="lazyLoadingEnabled" value="true"/>
</settings>

延迟加载原理分析:
延迟加载主要是通过动态代理的形式实现,通过代理拦截到指定⽅法,执⾏数据加载。
它的原理是,使⽤ CGLIB 或 Javassist( 默认 ) 创建⽬标对象的代理对象。当调⽤代理对象的延迟加载属性的 getting ⽅法时,进⼊拦截器⽅法。⽐如调⽤a.getB().getName()⽅法,会进⼊拦截器的invoke()⽅法,发现a.getB()需要延迟加载时,那么就会单独发送事先保存好的查询关联 B对象的 SQL ,把 B 查询上来,然后调⽤ a.setB(b) ⽅法,于是a对象的b属性就有值了,接着完成 a.getB().getName() ⽅法的调⽤。

延迟加载源码阅读:

  1. setting配置加载:
public class Configuration {
	/** aggressiveLazyLoading:
	*当开启时,任何⽅法的调⽤都会加载该对象的所有属性。否则,每个属性会按需加载(参考
	lazyLoadTriggerMethods).
	*默认为true
	* */
	protected boolean aggressiveLazyLoading;
	/**
	*延迟加载触发⽅法
	*/
	protected Set<String> lazyLoadTriggerMethods = new HashSet<String> (Arrays.asList(new String[] { "equals", "clone", "hashCode", "toString" }));
	/** 是否开启延迟加载 */
	protected boolean lazyLoadingEnabled = false;
	/**默认使⽤Javassist代理⼯⼚
	*@param proxyFactory
	*/
	public void setProxyFactory(ProxyFactory proxyFactory) { if (proxyFactory == null) {
	proxyFactory = new JavassistProxyFactory();
	}
	this.proxyFactory = proxyFactory;
}
  1. 延迟加载代理对象创建:

Mybatis的查询结果是由ResultSetHandler接⼝的handleResultSets()⽅法处理的。ResultSetHandler接⼝只有⼀个实现,DefaultResultSetHandler。

// 创建映射后的结果对象
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
    // useConstructorMappings ,表示是否使用构造方法创建该结果对象。此处将其重置
    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) {
            // 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;
}
  1. 默认采⽤javassistProxy进⾏代理对象的创建
protected ProxyFactory proxyFactory = new JavassistProxyFactory(); 
  1. JavasisstProxyFactory实现:
static Object crateProxy(Class<?> type, MethodHandler callback, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
     // 创建 javassist ProxyFactory 对象
     ProxyFactory enhancer = new ProxyFactory();
     // 设置父类
     enhancer.setSuperclass(type);

     // 根据情况,设置接口为 WriteReplaceInterface 。和序列化相关,可以无视
     try {
         type.getDeclaredMethod(WRITE_REPLACE_METHOD); // 如果已经存在 writeReplace 方法,则不用设置接口为 WriteReplaceInterface
         // ObjectOutputStream will call writeReplace of objects returned by writeReplace
         if (log.isDebugEnabled()) {
             log.debug(WRITE_REPLACE_METHOD + " method was found on bean " + type + ", make sure it returns this");
         }
     } catch (NoSuchMethodException e) {
         enhancer.setInterfaces(new Class[]{WriteReplaceInterface.class}); // 如果不存在 writeReplace 方法,则设置接口为 WriteReplaceInterface
     } catch (SecurityException e) {
         // nothing to do here
     }

     // 创建代理对象
     Object enhanced;
     Class<?>[] typesArray = constructorArgTypes.toArray(new Class[constructorArgTypes.size()]);
     Object[] valuesArray = constructorArgs.toArray(new Object[constructorArgs.size()]);
     try {
         enhanced = enhancer.create(typesArray, valuesArray);
     } catch (Exception e) {
         throw new ExecutorException("Error creating lazy proxy.  Cause: " + e, e);
     }

     // 设置代理对象的执行器
     ((Proxy) enhanced).setHandler(callback);
     return enhanced;
 }
  1. 代理对象执⾏
@Override
public Object invoke(Object enhanced, Method method, Method methodProxy, Object[] args) throws Throwable {
    final String methodName = method.getName();
    try {
        synchronized (lazyLoader) {
            // 忽略 WRITE_REPLACE_METHOD ,和序列化相关
            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 {
            	//延迟加载数量⼤于0
                if (lazyLoader.size() > 0 && !FINALIZE_METHOD.equals(methodName)) {
                    // 加载所有延迟加载的属性
                    if (aggressive || lazyLoadTriggerMethods.contains(methodName)) {
                    	//⼀次全部加载
                        lazyLoader.loadAll();
                    // 如果调用了 setting 方法,则不在使用延迟加载
                    } else if (PropertyNamer.isSetter(methodName)) {
                        final String property = PropertyNamer.methodToProperty(methodName);
                        lazyLoader.remove(property); // 移除
                    // 如果调用了 getting 方法,则执行延迟加载
                    } 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);
    }
}
}

最后

以上就是哭泣鞋子为你收集整理的MyBatis 延迟加载的全部内容,希望文章能够帮你解决MyBatis 延迟加载所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(43)

评论列表共有 0 条评论

立即
投稿
返回
顶部