12、高级映射、延迟加载

一、准备工作

  • 我们之前的操作都是针对一张表来完成的,如果需要处理多张存在关联关系的表 >> 高级映射
  • 我们提前准备两张表:t_clazz、t_student
     
    插入几条数据:
     
  • 创建新模块:mybatis-010-advanced-mapping
  • 修改pom配置文件【打包方式、添加依赖】
  • 建包【pojo、utils、mapper、test】、copy那些固定的文件
  • 创建pojo类两个、接口两个、映射文件两个、测试类两个
     

  • 高级映射分为两种:多对一映射 和 一对多映射【谁在前谁为主表/类】
  • 无论是一对多还是多对一,总是为多的一方添加外键

二、多对一映射

🌔 1、学生与班级之间的关系是怎样的呢?
 
所以我们可以得出结论:

(1)t_student 表是主表,Student类的实例是主对象
(2)t_clazz 表是副表, Clazz类的实例是副对象
(3)我们在Student类中添加Clazz类的对象,来模拟外键

🌔 2、我们如何实现这种多对一的表的查询呢?

  • 第一种方式:一条SQL语句,级联属性映射。
  • 第二种方式:一条SQL语句,association。
  • 第三种方式:两条SQL语句,分步查询。【常用,可复用–支持懒加载】

🌔 3、如何实现级联属性映射呢?

  • 老样子三个文件:接口方法、SQL语句、测试方法

(1)接口方法:参数只有我们想要查询的学生id,返回学生信息、附带班级信息

    /**
* 根据id获取学生信息,同时获得学生关联的班级信息
* @param id 学号
* @return 包含班级对象的学生对象
*/
Student selectById(Integer id);

(2)SQL语句:使用了属性名和字段名的映射,基于班级号左外连接,筛选指定id的学生信息

    <!--一条SQL语句,级联属性映射-->
    <resultMap id="studentResultMap" type="Student">
        <id property="sid" column="sid" />
        <result property="sname" column="sname" />
        <result property="clazz.cid" column="cid" />
        <result property="clazz.cname" column="cname" />
    </resultMap>

    <select id="selectById" resultMap="studentResultMap">
        select
            s.sid, s.sname, c.cid, c.cname
        from
            t_student s left join t_clazz c on s.cid = c.cid
        where
            s.sid = #{

     sid}
    </select>

(3)测试方法:就传个id,再打印一下学生信息

    @Test
    public void testSelectById(){

        SqlSession sqlSession = SqlSessionUtil.openSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        Student student = mapper.selectById(4);
        System.out.println(student);
        sqlSession.close();
    }

&nbsp;

🌔 4、如何使用 association 来完成映射呢?

  • 属于一种关联映射与前一种类似,只需要修改 resultMap 中的信息即可
<resultMap id="studentResultMapAssociation" type="Student">
        <!--Student类的映射-->
        <id property="sid" column="sid"/>
        <result property="sname" column="sname" />
        <!--使用association标签
            property我们提供映射POJO类的属性名
            javaType用来指定要映射的Java类型
        -->
        <association property="clazz" javaType="Clazz">
            <id property="cid" column="cid" />
            <result property="cname" column="cname" />
        </association>
</resultMap>
  • SQL中select语句的内容不变,给出测试程序
    @Test
    public void testSelectByIdAssociation(){

        SqlSession sqlSession = SqlSessionUtil.openSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        Student student = mapper.selectByIdAssociation(4);
        System.out.println(student);
        sqlSession.close();
    }

&nbsp;
🌔 5、如何通过分步查询来完成这种多对一的查询?

  • 要写两条SQL语句,同时也要在两个接口中写方法
  • 重点在于利用resultMap完成一条SQL调用另一条SQL,并完成查询结果的传递

(1)我们在两个接口中写的方法

// StudentMapper
/**
* 分步查询第一步
* @param id 学生id
* @return 获得学生对象
*/
Student selectByIdStep1(Integer id);
// ClazzMapper
/**
* 分步查询第二步
* @param id 班级号
* @return 班级对象
*/
Clazz selectByIdStep2(Integer id);

(2)我们在ClazzMapper.xml 中写的SQL语句【这部分简单,先给出】

<!--此处没有再写resultMap因为属性名和字段名一致-->
<select id="selectByIdStep2" resultType="Clazz">
    select cid, cname from t_clazz where cid = #{

     cid}
</select>

(3)我们在StudentMapper.xml 中写的SQL语句 【重点】

<!--分步查询-->
<resultMap id="studentResultMapByStep" type="Student">
    <id property="sid" column="sid" />
    <result property="sname" column="sname" />
    <association property="clazz"
                 select="com.powernode.mybatis.mapper.ClazzMapper.selectByIdStep2"
                 column="cid"/>
</resultMap>
<select id="selectByIdStep1" resultMap="studentResultMapByStep">
    select sid, sname, cid from t_student where sid = #{

     sid}
</select>
  • resultMap中,刚开始还是正常给出属性名和列名之间的映射

  • association标签中,property 仍然是我们需要映射的对象

  • select 属性代表我们要调用的SQL语句,后面为全限定类名+SQL的 id

  • colum 属性代表我们当前的查询得到的指定的值传递给我们要调用的SQL语句 【给第二步传值】

(4)编写测试方法:

@Test
public void testSelectByIdStep1(){

    SqlSession sqlSession = SqlSessionUtil.openSession();
    StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
    Student student = mapper.selectByIdStep1(5);
    System.out.println(student);
    sqlSession.close();
}

&nbsp;
&nbsp;
🌔6、分步查询有什么优点?

  • SQL语句的可复用强:因为我们可以单独使用两个片段 >> 可复用

  • 支持延迟加载 >> 开启延迟加载后,是否调用指定的SQL语句就看是否使用了那条SQL语句查询的结果

  • 那么我们如何开启延迟加载呢?

  • 我们只需要在调用其他SQL语句的association标签中添加一个属性:fetchType="lazy"

  • 开启后可以提高mybatis的效率

  • 当前的延迟加载只针对当前的SQL有效,我们也可以直接开启全局的延迟加载

在核心配置文件的 settings / setting 标签下配置

<settings>
    <setting name="lazyLoadingEnabled" value="true">
</settings>
  • 开启全局延迟加载后,所有分步查询都会自动启用延迟加载,加入当前的resultMap不想用延迟加载怎么办?

我们还是在 association 的标签中通过 fetchType 属性来取消延迟加载,属性值为 eger


三、一对多映射

🌔 1、一对多的映射原理是什么呢?

&nbsp;

  • 就是说我们一个班级可以有很多个学生,一个学生只能属于一个班级
  • 之前我们说过,谁在前谁为主表,所以 t_clazz 表是主表,t_student 副表
  • Clazz的对象是一个集合,存储所有班级内的学生信息
  • 我们可以通过两种方式来实现这种一对多的映射:Collection、分步

🌔 2、如何通过collection标签来完成映射?

(1)我们需要修改Clazz类,添加一个stusStudent集合的私有属性【并修改对应的配置信息】

private List<Student> stus;

(2)在ClazzMapper接口中设计方法

Clazz selectByCollection(Integer id);

(3)在ClazzMapper.xml文件中设计对应的resultMapSQL语句

<!--在写一个resultMap,先根据班级号查询,再调用学生的表查询-->
    <resultMap id="clazzResultMap" type="Clazz">
        <id property="cid" column="cid" />
        <result property="cname" column="cname" />
        <!--一对多
            ofType 代表集合中元素类型
            property 代表我们的集合
        -->
        <collection property="stus" ofType="student">
            <id property="sid" column="sid" />
            <result property="sname" column="sname" />
        </collection>
    </resultMap>
    <select id="selectByCollection" resultMap="clazzResultMap">
        select c.cid, c.cname, s.sid, s.sname from t_clazz c left join t_student s on c.cid = s.cid where c.cid = #{

     cid}
</select>
  • 感觉和多对一的第二个方法有点类似
  • 通过集合标签,指定我们要处理的集合和对应的类型

(4)测试方法:

@Test
public void testSelectByCollection(){

    SqlSession sqlSession = SqlSessionUtil.openSession();
    ClazzMapper mapper = sqlSession.getMapper(ClazzMapper.class);
    Clazz clazz = mapper.selectByCollection(1001);
    System.out.println(clazz);
    sqlSession.close();
}

&nbsp;

🌔 3、如何通过分步的方法实现映射?

(1)写两个接口方法

// ClazzMapper
Clazz selectByStep1(Integer cid);
//StudentMapper
List<Student> selectByCidStep2(Integer cid);

(2)对应映射文件的SQL:

// ClazzMapper.xml
<resultMap id="clazzResultMapStep" type="Clazz">
        <id property="cid" column="cid" />
        <result property="cname" column="cname" />
        <collection property="stus"
                    select="com.powernode.mybatis.mapper.StudentMapper.selectByCidStep2"
                    column="cid"/>
</resultMap>
<select id="selectByStep1" resultMap="clazzResultMapStep">
        select * from t_clazz where cid = #{

     cid}
</select>
// StudentMapper.xml
<select id="selectByCidStep2" resultType="Student">
    select sid, sname from t_student where cid = #{

     cid}
</select>

(3)测试方法:

@Test
public void testSelectByStep1(){

    SqlSession sqlSession = SqlSessionUtil.openSession();
    ClazzMapper mapper = sqlSession.getMapper(ClazzMapper.class);
    Clazz clazz = mapper.selectByStep1(1000);
    System.out.println(clazz);
    sqlSession.close();
}

&nbsp;

(4)这种分步方法也支持延迟加载,可以局部配置也可以全局配置