MyBatis的延迟加载

  问题:在一对多的关系中,例:有一个班级,其中有100个学生,在查询班级的时候要不要把关联的学生查询出来,查询学生的时候要不要把关联的班级查询出来?
  解答:在查询班级的时候,学生的信息应该是什么时候使用什么时候去查询,在查询学生时,班级的信息应该是随着学生查询时一起查询出来。

一、延迟加载

  1. 概念:MyBatis中的延迟加载也称为懒加载,是指在进行表的关联查询时,按照设置延迟规则推迟对关联对象的select查询。在真正使用数据的时候才发起查询,不用的时候不查询关联的数据,延迟加载又叫做按需查询。例如,在进行一对多查询的时候,只查询出一方,当程序中需要多方数据时,MyBatis在发出sql语句进行查询,这样子延迟加载就可以减少数据库压力,MyBatis的延迟加载只是对关联对象的查询有延迟设置,对于主加载对象都是直接执行查询语句的。

  2. MyBatis中延迟加载的条件:resultMap可以实现高级映射,例:使用association、collection实现一对一及一对多映射,association、collection具备延迟加载功能。

  3. 延迟加载的好处:先从单表查询,需要时再从关联表查询,大大提高数据库性能,因为查询单表比关联查询多张表速度要快。

  4. 使用场景:在对应的四种关系表中,一对多、多对多通常情况下采用延迟加载,多对一、一对一、通常采用立即加载。

  5. 延迟加载的应用要求:关联对象的查询与主加载对象的查询必须是分别进行的select语句,不能是使用多表连接所进行的select查询,因为多表连接查询,实质是对一张表的查询,对由多个表连接后形成的一张表的查询,会一次性将多张表的所有信息查询出来。

二、开启延迟加载功能

  MyBatis中实现查询方法的延迟加载,在MyBatis
的配置文件中通过设置settings的lazyLoadingEnabled属性为true进行开启全局的延迟加载,通过aggressiveLazyLoading属性开启立即加载。

设置名 描述 有效值 默认值
lazyLoadingEnabled 延迟加载的全局开关,当开启时所有关联对象都会延迟加载,特定关联关系可以通过设置fetchType属性来覆盖该项的开关状态。 true|false false
aggressiveLazyLoading 开启时,任一方法的调用都会加载该对象的所有延迟加载属性。 否则,每个延迟加载属性会按需加载(参考 lazyLoadTriggerMethods)。 true|false false(在3.4.1及之前的版本默认值为true)
lazyLoadTriggerMethods 指定对象的哪些方法触发一次延迟加载。 用逗号分隔的方法列表。 equals,clone,hashCode,toString
  <settings>
        <!-- 开启延迟加载主要是前两个配置-->
        <!-- 延迟加载,默认是false-->
        <setting name="lazyLoadingEnabled" value="true" />
        <!-- 当为true时,所有延迟属性都会立即加载,为false时,调用这些延迟属性时才会查询和加载-->
        <setting name="aggressiveLazyLoading" value="false" />

        <!-- 最好配置为空,这样可以最大限度延迟加载-->
        <!-- 当前配置的意思为当调用getId时延迟加载-->
        <!-- 可以不配置-->
        <setting name="lazyLoadTriggerMethods" value="getId"/>
    </settings>

三、延迟加载的实现

  1. 在resultMap中使用association或者collection,即可使用延迟加载。

  2. 延迟加载需要多个select语句,不能是一条多表连接的select语句。

实现班级学生案例:

  1. 创建实体类 班级类以及学生类
班级类:

public class Clazz {
    private int id;
    private String name;
    private List<Student> students;

    @Override
    public String toString() {
        return "Clazz{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", students=" + students +
                '}';
    }
    
    get和set方法省略...





学生类:

public class Student {
    private int id;
    private String name;
    private String sex;
    private int clazz;

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                ", clazz=" + clazz +
                '}';
    }

    get和set方法省略...
  1. 创建StudentMapper和ClazzMapper接口
ClazzMapper接口:

public interface ClazzMapper {
    /**
     * 根据班级编号查询班级
     * @param id
     * @return
     */
    Clazz selectById(int id);
}



StudentMapper接口:

public interface StudentMapper {
    /**
     * 根据学生编号查找学生
     * @param id
     * @return
     */
    Student selectById(int id);

    /**
     * 根据学生的班级号查找学生
     * @param id
     * @return
     */
    List<Student> selectByClazz(int id);
}

  1. 创建ClazzMapper.xml和StudentMapper.xml
ClazzMapper.xml:

    <!-- 根据班级编号查找班级-->
    <select id="selectById" resultMap="rm">
        select * from clazz where id=#{id}
    </select>

    <!-- 配置映射,根据学生班级号查找学生放进班级里-->
    <resultMap id="rm" type="clazz" autoMapping="true">
        <id column="id" property="id"></id>
        <collection property="students" column="id" select="dao.StudentMapper.selectByClazz"></collection>
    </resultMap>



StudentMapper.xml:

    <!-- 根据学生id查找学生-->
    <select id="selectById" resultType="student">
        select * from student where id=#{id}
    </select>

    <!-- 根据学生班级号查找学生-->
    <select id="selectByClazz" resultType="student">
        select * from student where clazz=#{id}
    </select>
  1. 测试:
public class Test {
    public static void main(String[] args) {
        Clazz clazz = new ClazzService().selectById(1); // 1
        System.out.println(clazz.getId()); // 2
        System.out.println(clazz.getName()); // 3
        System.out.println(clazz.getStudents()); // 4
    }
}

当注释掉第4句时,因为没有要求查找学生,按照延迟加载的特性,程序只会查询班级的信息,而不会查询学生的信息,所以只有一条sql语句,就是根据班级编号查找班级的select,并没有查找学生的select:

当放开第四句时,因为clazz.getStudents()需要student数据,所以会执行第二条sql语句将student的信息查询到:

posted @ 2021-07-08 15:34  逍遥客灬  阅读(2380)  评论(0)    收藏  举报