亦余心之所善兮,虽九死其犹未悔。这篇文章主要讲述MyBatis从入门到精通—源码剖析之延迟加载源码细节相关的知识,希望能为你提供帮助。
什么是延迟加载?在开发过程中很多时候我们并不需要总是在加载?户信息时就?定要加载他的订单信息。此时就是我
们所说的延迟加载。
举个栗?:
延迟加载
就是在需要?到数据时才进?加载,不需要?到数据时就不加载数据。延迟加载也称懒加载。
- 优点:
- 缺点:
在多表中:
?对多,多对多:通常情况下采?延迟加载
?对?(多对?):通常情况下采??即加载
注意:
延迟加载是基于嵌套查询来实现的。
实现 局部延迟加载
在association和collection标签中都有?个fetchType属性,通过修改它的值,可以修改局部的加载策略。
<
!-- 开启?对多 延迟加载 -->
<
resultMap id="userMap" type="user">
<
id column="id" property="id">
<
/id>
<
result column="username" property="username">
<
/result>
<
result column="password" property="password">
<
/result>
<
result column="birthday" property="birthday">
<
/result>
<
!--
fetchType="lazy" 懒加载策略
fetchType="eager" ?即加载策略
-->
<
collection property="orderList" ofType="order" column="id" select="com.zjq.dao.OrderMapper.findByUid" fetchType="lazy">
<
/collection>
<
/resultMap>
<
select id="findAll" resultMap="userMap">
SELECT * FROM `user`
<
/select>
全局延迟加载
在Mybatis的核?配置?件中可以使?setting标签修改全局的加载策略。
<
settings>
<
!--开启全局延迟加载功能-->
<
setting name="lazyLoadingEnabled" value="https://www.songbingjia.com/android/true"/>
<
/settings>
注意:
<
!-- 关闭?对? 延迟加载 -->
<
resultMap id="orderMap" type="order">
<
id column="id" property="id">
<
/id>
<
result column="ordertime" property="ordertime">
<
/result>
<
result column="total" property="total">
<
/result>
<
!--
fetchType="lazy" 懒加载策略
fetchType="eager" ?即加载策略
-->
<
association property="user" column="uid" javaType="user"
select="com.zjq.dao.UserMapper.findById" fetchType="eager">
<
/association>
<
/resultMap>
<
select id="findAll" resultMap="orderMap">
SELECT * from orders
<
/select>
延迟加载原理实现它的原理是,使? CGLIB 或 Javassist( 默认 ) 创建?标对象的代理对象。当调?代理对象的延迟加载属性的 getting ?法时,进?拦截器?法。?如调? a.getB().getName() ?法,进?拦截器的invoke(...) ?法,发现 a.getB() 需要延迟加载时,那么就会单独发送事先保存好的查询关联 B对象的 SQL ,把 B 查询上来,然后调?a.setB(b) ?法,于是 a 对象 b 属性就有值了,接着完成a.getB().getName() ?法的调?。这就是延迟加载的基本原理
总结:延迟加载主要是通过动态代理的形式实现,通过代理拦截到指定?法,执?数据加载。
延迟加载原理(源码剖析)MyBatis延迟加载主要使?:Javassist,Cglib实现,类图展示:
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
this.useConstructorMappings = false;
// reset previous mapping result
final List<
Class<
?>
>
constructorArgTypes = new ArrayList<
Class<
?>
>
();
final List<
Object>
constructorArgs = new ArrayList<
Object>
();
//创建返回的结果映射的真实对象
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进?代理对象的创建。
public class Configuration
protected ProxyFactory proxyFactory = new JavassistProxyFactory();
JavasisstProxyFactory实现
public class JavassistProxyFactory implements org.apache.ibatis.executor.loader.ProxyFactory
/**
* 接?实现
* @param target ?标结果对象
* @param lazyLoader 延迟加载对象
* @param configuration 配置
* @param objectFactory 对象??
* @param constructorArgTypes 构造参数类型
* @param constructorArgs 构造参数值
* @return
*/
@Override
public Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<
Class<
?>
>
constructorArgTypes, List<
Object>
constructorArgs)
return EnhancedResultObjectProxyImpl.createProxy(target, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
/**
* 创建代理对象
* @param type
* @param callback
* @param constructorArgTypes
* @param constructorArgs
* @return
*/
static Object crateProxy(Class<
?>
type, MethodHandler callback, List<
Class<
?>
>
constructorArgTypes, List<
Object>
constructorArgs) ProxyFactory enhancer = new ProxyFactory();
enhancer.setSuperclass(type);
try
//通过获取对象?法,判断是否存在该?法
type.getDeclaredMethod(WRITE_REPLACE_METHOD);
// 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);
catch (SecurityException e)
// nothing to do hereObject 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;
/**
* 内部类代理对象实现,核?逻辑执?
*/
private static class EnhancedResultObjectProxyImpl implements MethodHandler private final Class<
?>
type;
private final ResultLoaderMap lazyLoader;
private final boolean aggressive;
private final Set<
String>
lazyLoadTriggerMethods;
private final ObjectFactory objectFactory;
private final List<
Class<
?>
>
constructorArgTypes;
private final List<
Object>
constructorArgs;
private EnhancedResultObjectProxyImpl(Class<
?>
type, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<
Class<
?>
>
constructorArgTypes, List<
Object>
constructorArgs)
this.type = type;
this.lazyLoader = lazyLoader;
this.aggressive = configuration.isAggressiveLazyLoading();
this.lazyLoadTriggerMethods = configuration.getLazyLoadTriggerMethods();
this.objectFactory = objectFactory;
this.constructorArgTypes = constructorArgTypes;
this.constructorArgs = constructorArgs;
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;
/**
* 代理对象执?
* @param enhanced 原对象
* @param method 原对象?法
* @param methodProxy 代理?法
* @param args ?法参数
* @return
* @throws Throwable
*/
@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
//延迟加载数量?于0
if (lazyLoader.size() >
0 &
&
!FINALIZE_METHOD.equals(methodName))
//aggressive ?次加载性所有需要要延迟加载属性或者包含触发延迟加载?法
if (aggressive || lazyLoadTriggerMethods.contains(methodName))
//?次全部加载
lazyLoader.loadAll();
else if (PropertyNamer.isSetter(methodName))
//判断是否为set?法,set?法不需要延迟加载
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);
注意事项
IDEA调试问题: 当配置aggressiveLazyLoading=true,在使?IDEA进?调试的时候,如果断点打到代理执?逻辑当中,你会发现延迟加载的代码永远都不能进?,总是会被提前执?。 主要产?的原因在aggressiveLazyLoading,因为在调试的时候,IDEA的Debuger窗体中已经触发了延迟加载对象的?法。
【MyBatis从入门到精通—源码剖析之延迟加载源码细节】
文章图片
推荐阅读
- 反调试 - CheckRemoteDebuggerPresent
- 反调试 - IsDebuggerPresent
- 线程池,我是谁(我在哪儿?)
- Windows 异常机制
- #导入Word文档图片# Linux下内核微线程tasklet
- 机器学习之用Python实现最小二乘法预测房价,进行额度预测
- x64驱动 遍历驱动模块
- Python 迭代器介绍及其作用
- WinDbg 配置网络调试