MyBatis从入门到精通—源码剖析之延迟加载源码细

什么是延迟加载?

在开发过程中很多时候我们并不需要总是在加载?户信息时就?定要加载他的订单信息。此时就是我们所说的延迟加载。举个栗?:

在?对多中,当我们有?个?户,它有个100个订单在查询?户的时候,要不要把关联的订单查出来?在查询订单的时候,要不要把关联的?户查出来?回答在查询?户时,?户下的订单应该是,什么时候?,什么时候查询。在查询订单时,订单所属的?户信息应该是随着订单?起查询出来。

延迟加载就是在需要?到数据时才进?加载,不需要?到数据时就不加载数据。延迟加载也称懒加载。

优点:

先从单表查询,需要时再从关联表去关联查询,??提?数据库性能,因为查询单表要?关联查询多张表速度要快。

缺点:

因为只有当需要?到数据时,才会进?数据库查询,这样在?批量数据查询时,因为查询?作也要消耗时间,所以可能造成?户等待时间变?,造成?户体验下降。在多表中:?对多,多对多:通常情况下采?延迟加载?对?(多对?):通常情况下采??即加载注意:延迟加载是基于嵌套查询来实现的。

实现局部延迟加载

在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=”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 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; } /** * 内部类代理对象实现,核?逻辑执? */ 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从入门到精通—源码剖析之延迟加载源码细

相关文章:

你感兴趣的文章:

标签云: