我是靠谱客的博主 糟糕丝袜,最近开发中收集的这篇文章主要介绍MyBatis 如何支持延迟加载?现实原理是什么?,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

支持延迟加载的配置:
在配置文件的标签内设置参数

  • lazyLoadingEnabled:延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态

  • aggressiveLazyLoading:开启时,任一方法的调用都会加载该对象的所有延迟加载属性。否则,每个延迟加载属性会按需加载

  • lazyLoadTriggerMethods:指定对象的哪些方法触发一次延迟加载

resultMap 中配置 <association> 或 <collection>

 

配置与测试示例

//配置文件
< settings>
    < setting name="lazyLoadingEnabled" value="true"/>
    < setting name="aggressiveLazyLoading" value="false"/>
    < setting name="lazyLoadTriggerMethods" value=""/>
< /settings>


//Mapper xml
< select id="selectUserWithLazyInfo" resultMap="UserWithLazyInfo">
    select * from user where id = 1
< /select>

< resultMap id="UserWithLazyInfo" type="constxiong.po.User">
    < id property="id" column="id"/>
    < result property="name" column="name"/>
    < association property="info" javaType="constxiong.po.Info" select="constxiong.mapper.InfoMapper.selectInfoByUserId" column="id"/>
< /resultMap>


//InfoMapper
public interface InfoMapper {
    @Select("select * from info where user_id = #{userId}")
    @Results(value = {@Result(column="user_id", property = "userId")})
    Info selectInfoByUserId(int userId);
}


//测试代码
System.out.println("------ selectUserWithLazyInfo ------");
User user = userMapper.selectUserWithLazyInfo();
System.out.println(user);
System.out.println(user.getInfo());


//打印 User 对象里的 Info 为空,使用 getInfo 能够查询对应的值
------ selectUserWithLazyInfo ------
User{id=1, name='ConstXiong1', mc='null', info=null, articles=null}
Info{userId=1, name=大熊}

 

实现原理:

支持延迟加载是通过字节码增强实现的,MyBatis 3.3 及以上默认使用了 javassist,3.3 以前使用 cglib 实现。

我本地用的 MyBatis 3.5.5,使用了 javassist 增强,核心源码如下

//DefaultResultSetHandler getRowValue 获取每条的查询数据,resultMap 中如果包含懒加载 rowValue 在 createResultObject 方法通过 javassist 代理增强
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
    final ResultLoaderMap lazyLoader = new ResultLoaderMap();
    //对象数据,通过 javassist 代理增强
    Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
    if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
        final MetaObject metaObject = configuration.newMetaObject(rowValue);
        boolean foundValues = this.useConstructorMappings;
        if (shouldApplyAutomaticMappings(resultMap, false)) {
            foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
        }
        //根据从数据库查询到的 resultSet,根据 resultMap 通过反射设置 rowValue 的值
        foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
        foundValues = lazyLoader.size() > 0 || foundValues;
        rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
    }
    return rowValue;
}

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<>();
    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) {
            //如果返回对象的属性中包含懒加载,使用 javassist 代理增强,当设置属性值时被代理到 JavassistProxyFactory 的 invoke 方法
            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;
}
//JavassistProxyFactory 的 invoke 方法
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)) {
                ...
            } else {
                if (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)) {
                        //测试代码中 user.getInfo() 方法的调用,在此执行懒加载查询关联 SQL 设置 info 属性
                        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);
    }
}

 

完整 Demo:

https://javanav.com/val/973ded541e9244aa8b3169b9fb869d60.html

 

 


【Java面试题与答案】整理推荐

  • 基础与语法
  • 集合
  • 网络编程
  • 并发编程
  • Web
  • 安全
  • 设计模式
  • 框架
  • 算法与数据结构
  • 异常
  • 文件解析与生成
  • Linux
  • MySQL
  • Oracle
  • Redis
  • Dubbo

 

最后

以上就是糟糕丝袜为你收集整理的MyBatis 如何支持延迟加载?现实原理是什么?的全部内容,希望文章能够帮你解决MyBatis 如何支持延迟加载?现实原理是什么?所遇到的程序开发问题。

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

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

评论列表共有 0 条评论

立即
投稿
返回
顶部