本文主要介绍下mybatis的延迟加载,从原理上介绍下怎么使用、有什么好处能规避什么问题。延迟加载一般用于级联查询(级联查询可以将主表不能直接查询的数据使用自定义映射规则调用字表来查,主查询查完之后通过某个column列或多个列将查询结果传递给子查询,子查询再根据主查询传递的参数进行查询,最后将子查询结果进行映射)。mybatis的懒加载是通过创建代理对象来实现的,只有当调用getter等方法的时候才会去查询子查询,查询后完成设值再获取值。

1. 什么时候会创建代理对象

  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<>();
    // 创建result接收对象
    Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
    if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
      // 处理其他属性properties
      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;
  }

通过mybatis代码propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()发现只有当存在嵌套查询select子句和isLazy=true的时候才会创建代理,那么isLazy=true是什么条件,从创建ResultMapping的代码中可以看到boolean lazy = "lazy".equals(context.getStringAttribute("fetchType", configuration.isLazyLoadingEnabled() ? "lazy" : "eager"));只有手动设置fetchType=lazy或者全局设置configuration的lazyLoadingEnabled=true,两者缺一不可。

关于代理是通过Javassist创建的,下面有一个简单的例子

public class HelloMethodHandler implements MethodHandler {

  private Object target;

  public HelloMethodHandler(Object o) {
    this.target = o;
  }

  @Override
  public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable {
    String methodName = thisMethod.getName();
    if (methodName.startsWith("get")) {
      System.out.println("select database....");
      // 进行sql查询到结果并set设置值
      ((Student)self).setName("monian");
    }
    return proceed.invoke(self, args);
  }

  public static void main(String[] args) throws Exception {
    Student student = new Student();
    ProxyFactory proxyFactory = new ProxyFactory();
    proxyFactory.setSuperclass(Student.class);

    Constructor<Student> declaredConstructor = Student.class.getDeclaredConstructor();
    Object o = proxyFactory.create(declaredConstructor.getParameterTypes(), new Object[]{});
    ((Proxy)o).setHandler(new HelloMethodHandler(student));

    Student proxy = (Student)o;
    System.out.println(proxy.getName());
  }
}

mybatis的原理就是通过创建一个代理对象,当通过这个代理对象调用getter、is、equals、clone、toString、hashCode等方法时会调用select子查询,然后完成设置,最后取值就像早就获取到一样。

2. 如何使用

public class UserDO {

  private Integer userId;

  private String username;

  private String password;

  private String nickname;

  private List<PermitDO> permitDOList;

  public UserDO() {}
}
<resultMap id="BaseMap" type="org.apache.ibatis.study.entity.UserDO">
    <id column="user_id" jdbcType="INTEGER" property="userId" />
    <result column="username" jdbcType="VARCHAR" property="username" />
    <result column="password" jdbcType="VARCHAR" property="password" />
    <result column="nickname" jdbcType="VARCHAR" property="nickname"/>

    <collection property="permitDOList" column="user_id" select="getPermitsByUserId"
     fetchType="lazy">

    </collection>
</resultMap>

  <resultMap id="PermitBaseMap" type="org.apache.ibatis.study.entity.PermitDO">
    <id column="id" jdbcType="INTEGER" property="id"/>
    <result column="code" jdbcType="VARCHAR" property="code"/>
    <result column="name" jdbcType="VARCHAR" property="name"/>
    <result column="type" jdbcType="TINYINT" property="type"/>
    <result column="pid" jdbcType="INTEGER" property="pid"/>
  </resultMap>
  
      
   <select id="getByUserId2" resultMap="BaseMap">
    select * from user
    where user_id = #{userId}
  </select>

  <select id="getPermitsByUserId" resultMap="PermitBaseMap">
    select p.*
    from user_permit up
    inner join permit p on up.permit_id = p.id
    where up.user_id = #{userId}
  </select>

通过fetchType=lazy指定子查询getPermitsByUserId使用懒加载,这样的话就不用管全局配置lazyLoadingEnabled是true还是false了。当然这里可以直接用多表关联查询不使用子查询,使用方法在上一篇文章

测试代码

public class Test {

  public static void main(String[] args) throws IOException {

    try (InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml")) {
      // 构建session工厂 DefaultSqlSessionFactory
      SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
      SqlSession sqlSession = sqlSessionFactory.openSession();
      UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
      UserDO userDO = userMapper.getByUserId2(1);
      System.out.println(userDO);
    }
  }

}

结果如下,打了断点可以看到原userDO对象已被代理并且permitDOList是null需要调用get方法才会去查询拿到值,咳咳这边之前直接运行显示是已经把permitDOList查询出来了,想了半天啥原因后来才发现println会调用userDO对象的toString方法,而toString方法也会走代理方法直接去调用子查询的

3.延迟加载的好处

延迟加载主要能解决mybatis的N 1问题,什么是N 1问题其实叫1 N更为合理,以上面的业务例子来说就是假设一次查询出来10000个用户,那么还需要针对这10000个用户使用子查询getPermitsByUserId获取每个用户的权限列表,需要10000次查询,总共10001次,真实情况下你可能并不需要每个子查询的结果,这样就浪费数据库连接资源了。如果使用延迟加载的话就相当于不用进行这10000次查询,因为它是等到你真正使用的时候才会调用子查询获取结果。

以上就是一文了解mybatis的延迟加载的详细内容,更多关于mybatis延迟加载的资料请关注Devmax其它相关文章!

一文了解mybatis的延迟加载的更多相关文章

  1. vue自定义加载指令v-loading占位图指令v-showimg

    这篇文章主要为大家介绍了vue自定义加载指令和v-loading占位图指令v-showimg的示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  2. Mybatis分页插件PageHelper手写实现示例

    这篇文章主要为大家介绍了Mybatis分页插件PageHelper手写实现示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  3. Springboot集成mybatis实现多数据源配置详解流程

    在日常开发中,若遇到多个数据源的需求,怎么办呢?通过springboot集成mybatis实现多数据源配置,简单尝试一下,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  4. JavaScript实现动态加载删除表格

    这篇文章主要为大家详细介绍了JavaScript实现动态加载删除表格,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  5. Android自定义View实现圆形加载进度条

    这篇文章主要为大家详细介绍了Android自定义View实现圆形加载进度条,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  6. Mybatis结果集映射与生命周期详细介绍

    结果集映射指的是将数据表中的字段与实体类中的属性关联起来,这样 MyBatis 就可以根据查询到的数据来填充实体对象的属性,帮助我们完成赋值操作

  7. Android Flutter绘制有趣的 loading加载动画

    在网络速度较慢的场景,一个有趣的加载会提高用户的耐心和对 App 的好感。本篇我们利用Flutter 的 PathMetric来玩几个有趣的 loading 效果,感兴趣的可以动手尝试一下

  8. Spring Boot 整合持久层之MyBatis

    在实际开发中不仅仅是要展示数据,还要构成数据模型添加数据,这篇文章主要介绍了SpringBoot集成Mybatis操作数据库,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  9. Mybatis详解动态SQL以及单表多表查询的应用

    MyBatis的动态SQL是基于OGNL表达式的,它可以帮助我们方便的在SQL语句中实现某些逻辑,下面这篇文章主要给大家介绍了关于Mybatis超级强大的动态SQL语句的相关资料,需要的朋友可以参考下

  10. mybatis水平分表实现动态表名的项目实例

    本文主要介绍了mybatis水平分表实现动态表名的项目实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

随机推荐

  1. 基于EJB技术的商务预订系统的开发

    用EJB结构开发的应用程序是可伸缩的、事务型的、多用户安全的。总的来说,EJB是一个组件事务监控的标准服务器端的组件模型。基于EJB技术的系统结构模型EJB结构是一个服务端组件结构,是一个层次性结构,其结构模型如图1所示。图2:商务预订系统的构架EntityBean是为了现实世界的对象建造的模型,这些对象通常是数据库的一些持久记录。

  2. Java利用POI实现导入导出Excel表格

    这篇文章主要为大家详细介绍了Java利用POI实现导入导出Excel表格,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  3. Mybatis分页插件PageHelper手写实现示例

    这篇文章主要为大家介绍了Mybatis分页插件PageHelper手写实现示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  4. (jsp/html)网页上嵌入播放器(常用播放器代码整理)

    网页上嵌入播放器,只要在HTML上添加以上代码就OK了,下面整理了一些常用的播放器代码,总有一款适合你,感兴趣的朋友可以参考下哈,希望对你有所帮助

  5. Java 阻塞队列BlockingQueue详解

    本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景,通过实例代码介绍了Java 阻塞队列BlockingQueue的相关知识,需要的朋友可以参考下

  6. Java异常Exception详细讲解

    异常就是不正常,比如当我们身体出现了异常我们会根据身体情况选择喝开水、吃药、看病、等 异常处理方法。 java异常处理机制是我们java语言使用异常处理机制为程序提供了错误处理的能力,程序出现的错误,程序可以安全的退出,以保证程序正常的运行等

  7. Java Bean 作用域及它的几种类型介绍

    这篇文章主要介绍了Java Bean作用域及它的几种类型介绍,Spring框架作为一个管理Bean的IoC容器,那么Bean自然是Spring中的重要资源了,那Bean的作用域又是什么,接下来我们一起进入文章详细学习吧

  8. 面试突击之跨域问题的解决方案详解

    跨域问题本质是浏览器的一种保护机制,它的初衷是为了保证用户的安全,防止恶意网站窃取数据。那怎么解决这个问题呢?接下来我们一起来看

  9. Mybatis-Plus接口BaseMapper与Services使用详解

    这篇文章主要为大家介绍了Mybatis-Plus接口BaseMapper与Services使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  10. mybatis-plus雪花算法增强idworker的实现

    今天聊聊在mybatis-plus中引入分布式ID生成框架idworker,进一步增强实现生成分布式唯一ID,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部