MyBatis 延迟加载 & N+1问题

Mybatis 在解决一对多或者多对多的时候,共有两种解决方式:

  1. 嵌套查询
  2. 嵌套结果

嵌套查询:即将一条SQL中的多个子查询,分成多个SQL进行查询的方式;
嵌套结果:即使用一条SQL,在 SQL 中使用子查询的方式来完成。

      官方推荐使用嵌套结果的方式,可以减少与 SQL 的多次连接,减少资源消耗等;并且不推荐使用嵌套查询的方式,因为存在 N+1问题。这个问题,无论是 <association>标签还是 <collection>元素都会遇到。


在这里插入图片描述
接下来本文以 <association>标签为例来介绍一下 MyBatis 的延迟加载 & N+1问题。

       现在有两个表,分别是db_userdb_score。我们需要通过 id 与 user_id 字段关联,来查询学生的分数信息。如下图所示:
在这里插入图片描述

为了解决这个问题,我们可以采取两种方法:

        1.第一种方法是使用一条SQL语句,把学生信息及其分数信息一次性地查询出来。我们常用的 <resultMap> + <association> 形式用的就是这种方法,我们也推荐这样子的使用。

<resultMap id="ScoreResultMap" type="com.springboot.entity.User">
    <id column="id" property="id"/>
    <result column="username" property="username"/>
    <result column="age" property="age"/>
    <result column="address" property="address"/>
    <collection property="score" javaType="com.springboot.entity.Score">
        <id column="user_id" property="userId"/>
        <result column="course" property="course"/>
        <result column="score" property="score"/>
    </collection >
</resultMap>

<select id="queryUserScore" resultMap="ScoreResultMap">
    select
        t1.id,
        t1.username,
        t1.age,
        t1.address,
        t2.course,
        t2.score
    from
        db_user t1
        left join db_score t2 on t1.id = t2.user_id
</select>

执行结果:
在这里插入图片描述
        2. 第二种方法是使用MyBatis 的延迟加载机制。延迟加载机制需要在 mybatis-config.xml 核心配置文件 <settings>标签下添加如下配置。

<settings>
	<!-- 1.延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。默认 false  -->
	<setting name="lazyLoadingEnabled" value="true"/>
</settings>      

mapper.xml

<resultMap id="ScoreResultMap" type="com.springboot.entity.User">
    <id property="id" column="id"/>
    <result property="username" column="username"/>
    <result property="age" column="age"/>
    <result property="address" column="address"/>
    <!--property:A表字段   javaType:B表   column:A表要关联B表的字段(A表字段)  select:B表查询 SQL的 id -->
    <collection 
    		property="score" 
   			javaType="com.springboot.entity.Score" 
   			column="id" 
   			select="queryScore">
	</collection>
</resultMap>

<!-- A表 SQL -->
<select id="queryUser" resultMap="ScoreResultMap">
    select
        id, username, age, address
    from
    db_user
</select>

<!-- B表 SQL -->
<select id="queryScore" parameterType="int" resultType="com.springboot.entity.Score">
    select
        user_id, course, score
    from
        db_score
    where
        user_id = #{userId}
</select>

执行如下代码:

@Test
public void testQueryUserScore() throws Exception {
    //配置文件所在路径
    String resource = "mybatis/mybatis-config.xml";
    //读取MyBatis配置文件,转换成InputStream
    InputStream readerStream = Resources.getResourceAsStream(resource);
    //创建一个SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(readerStream);
    //创建一个SqlSession会话
    SqlSession sqlSession = sqlSessionFactory.openSession();

    try {
        //使用getMapper()方法,执行操作。
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        User user = userMapper.queryUser(3);

        //如果开启了延迟加载机制,会在使用的时候才发出SQL去查询
        System.out.println("id:"+user.getId());
		System.out.println("---------------------------");
		System.out.println("score:"+user.getScore());

    } finally {
        sqlSession.close();
    }
}

执行结果:
在这里插入图片描述


备注:

       在 settings 配置中,还有这么一个属性: aggressiveLazyLoading,当开启时,任何方法(包括equals、clone、hashCode、toString 等方法)的调用都会加载该对象的所有属性。默认为 false。

<settings>
	<!-- 2.当开启时,任何方法的调用都会加载该对象的所有属性。默认 false,可通过select标签的 fetchType来覆盖-->
	<setting name="aggressiveLazyLoading" value="false"/>
</settings>

执行结果:
在这里插入图片描述

延迟加载原理:动态代理

如图所示:
在这里插入图片描述
       Mybatis 创建具有延迟加载能力的对象所用到的代理工具,默认JAVASSIST,我们也可以自行设置使用 其他的动态代理机制,比如:cglib。

       在 settings 配置中,通过配置属性: proxyFactory,我们对其进行配置即可。(此处切记:需要导入Cglib Jar包)

<settings>
	<!--  Mybatis 创建具有延迟加载能力的对象所用到的代理工具,默认JAVASSIST -->
	<setting name="proxyFactory" value="CGLIB" />
</settings>

博主写作不易,来个关注呗

求关注、求点赞,加个关注不迷路 ヾ(◍°∇°◍)ノ゙

博主不能保证写的所有知识点都正确,但是能保证纯手敲,错误也请指出,望轻喷 Thanks♪(・ω・)ノ

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

扛麻袋的少年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值