概述
延迟加载:也叫做懒加载,就是用到数据时才进行查询加载,不需要用到时不进行加载。
优缺点:
- 优点:先从单表查询,需要时再从关联表去关联查询,⼤⼤提⾼数据库性能,因为查询单表要⽐关联查询多张表速度要快.
- 缺点:因为只有在用到数据时,才会去查询数据库,这样在大批量的数据查询时,查询工作也会消耗时间,可能造成用户请求响应慢,造成用户等待时间边长,使用户体验感变差。
适用场景:
- 延迟加载:在一对多、多对多时采用延迟加载;
- 立即加载:在一对一、多对一时采用立即加载。
延迟加载是基于嵌套查询来实现的。
局部加载的实现方式:
通过修改 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() ⽅法的调⽤。
延迟加载源码阅读:
- 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;
}
- 延迟加载代理对象创建:
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;
}
- 默认采⽤javassistProxy进⾏代理对象的创建
protected ProxyFactory proxyFactory = new JavassistProxyFactory();
- 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;
}
- 代理对象执⾏
@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 延迟加载所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复