MyBatis延迟加载策略深入探究

2022-11-13 13:11:18 加载 延迟 探究

有两种写法来表示查询信息,分别是链表查询和分步查询的方法。那么既然我么能用一个sql语句能够执行完,那为什么还要分开来写呢?

原因很简单:可以发现如果我们把他们连在一起那么他们就是一个多表查询语句,如果不放在一起执行,那那就是单独一个表的查询语句。但是这需要我们设置mybatis的延迟加载(懒加载)

分步查询的优点

**可以实现延迟加载,**但是必须在核心配置文件中设置全局配置信息

lazyLoadingEnabled:延迟加载的全局开关。当开启时,所有关联对象都会延迟加载

aggressiveLazyLoding:当开启时,任何方式的调用都会加载该对象的所有属性。否则,该属性会按需加载 ,此时就可以实现按需加载,需要获取的数据是什么,就只会执行相应的sql.此时会通过association和collection中的fetchType属性设置当前的分步查询是否使用懒加载

fetchType=“lazy(延迟加载) | eager(立即加载)”

在主配置文件当中设置延迟加载

延迟加载:在SqlMapConfig.xml中配置延迟加载文件

<settings>
    <setting name="logImpl" value="STDOUT_LOGGING" />  <!-- 打印日志到控制台上 -->
    <setting name="lazyLoadingEnabled" value="true"/>   
    <setting name="aggressiveLazyLoading" value="false"/>   <!-- false才会按需加载,不开启调用所有的对象 -->
</settings>

在studentDao.xml当中设置分步查询

测试结果

此时开启了懒加载,实现了分布查询

如何使用

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方法也会走代理方法直接去调用子查询的

延迟加载的好处

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

到此这篇关于MyBatis延迟加载策略深入探究的文章就介绍到这了,更多相关MyBatis延迟加载内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!

相关文章