(5)MyBatis一对多关联查询及延迟加载/懒加载的实现

MyBatis一对多关联查询

以一个班级有多个学生为例

首先创建两张表

班级表

CREATE TABLE `t_class` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(20) NULL,
  PRIMARY KEY (`id`));

学生表
cid为所在班级的主键

CREATE TABLE .`t_student` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(20) NULL,
  `age` INT NULL,
  `score` DOUBLE NULL,
  `cid` INT NULL,
  PRIMARY KEY (`id`));

创建实体类

package com.h3c.bean;

public class Student {

    private int id;
    private String name;
    private int age;
    private double score;

    //省略constructor、getter、setter、toString方法
    //注意constructor中的无参构造方法不能少
}
package com.h3c.bean;

public class MyClass {

    private int id;
    private String name;
    //属于这个班级的学生集合
    private List<Student> studentList;

    //省略constructor、getter、setter、toString方法
     //注意constructor中的无参构造方法不能少
}

注意如果二者是双向关联,即双方的属性中均有对方对象作为域属性出现,那么它们的toString()方法内部不要嵌套输出,否则会造成占内存溢出。

dao层

package com.h3c.dao;

import com.h3c.bean.MyClass;

public interface MyClassDao {

    MyClass selectMyClassById(int id);
	
	//List<Student> getStudents(int id);
}

编写MyClassMapper.xml映射文件

方式一:直接写多表联合查询sql
这种方式有几点需要说明:

  • 1、.用分页插件pageHelper的时候,该种方式会导致查询错乱。
  • 2、如果两张表中有字段名称相同,需要使用别名的方式区分,否则字段值可能被覆盖
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.h3c.dao.MyClassDao">

    <resultMap id="ClassMap" type="myClass">
        <!--MyClass中的基本属性-->
        <id column="id" property="id"/>
        <result column="name" property="name"/>
        <!--关联属性的映射关系-->
        <collection property="studentList" ofType="student">
            <id column="id" property="id"/>
            <result column="name" property="name"/>
            <result column="age" property="age"/>
            <result column="score" property="score"/>
        </collection>
    </resultMap>


    <select id="selectMyClassById" resultMap="ClassMap">
        SELECT c.id cid,c.name cname,s.id sid,s.name sname,s.age sage,s.score sscore
        FROM t_class c, t_student s
        WHERE c.id = s.cid and c.id=#{id}
    </select>
</mapper>

方式二:采用子查询的方式
这种方式不用关心多个表中有相同名称的字段的问题

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.h3c.dao.MyClassDao">
    <resultMap id="ClassMap" type="myClass">
        <id column="id" jdbcType="INTEGER" property="id"/>
        <result column="name" jdbcType="VARCHAR" property="name"/>
        <collection property="studentList"
                    column="id"
                    select="com.h3c.dao.MyClassDao.getStudents" />
     </resultMap>

     <resultMap id="StudentMap" type="student">
        <id column="id" jdbcType="INTEGER" property="id" />
        <result column="name" jdbcType="VARCHAR" property="name" />
        <result column="age" jdbcType="INTEGER" property="age" />
        <result column="score" jdbcType="DOUBLE" property="score"/>
        <result column="cid" jdbcType="INTEGER" property="cid"/>
     </resultMap>
     
     <select id="selectMyClassById" parameterType="com.h3c.bean.MyClass"
            resultMap="ClassMap">
        select id, name from t_class where id = #{id}
    </select>

    <select id="getStudents" resultMap="StudentMap" parameterType="int" >
        select id, name, age, score from t_student where cid = #{id}
    </select>
</mapper>

MyBatis延迟加载/懒加载

首先我们先思考一个问题,假设:一个用户有10个账户。
问题1:在查询用户的时候,是否要把关联的账户也查出来?
问题2:在查询账户的时候,是否要把关联的用户也查出来?

解答:
1、在查询用户的时候,用户下的账户信息应该是在我们需要使用的时候再去查询。
2、在查询账户的时候,账户的所属用户信息应该是随着账户查询时一起查询出来。

搞清楚这两个简单的问题后,我们就可以引出延迟加载和立即加载的特性。

  • 立即加载:执行完对主加载对象的 select 语句,马上执行对关联对象的 select 查询
  • 延迟加载:
    • 侵入式延迟: 执行对主加载对象的查询时,不会执行对关联对象的查询。但当要访问主加载对象的详情属性时,就会马上执行关联对象的select查询。
    • 深度延迟: 执行对主加载对象的查询时,不会执行对关联对象的查询。访问主加载对象的详情时也不会执行关联对象的select查询。只有当真正访问关联对象的详情时,才会执行对关联对象的 select 查询。

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

MyBatis延迟加载的实现

MyBatis通过在mybatis的全局配置文件中使用settings标签实现延迟加载,且只对于resultMap中的collection和association起作用。MyBatis延迟加载/懒加载需要使用多张表单独查询的方式,即上面的方式二。
下面来看一下具体配置参数:

参数说明默认值
lazyLoadingEnabled延迟加载的全局开关。当开启(值为true)时,所有关联对象都会延迟加载。 特定关联关系中可通过设置collection和association标签中的 fetchType 属性来覆盖该项的开关状态,配置为lazy之后是开启深度延迟,配置eager是不开启深度延迟。false
aggressiveLazyLoading值为true时,开启侵入式延迟加载;值为false时,开启深度延迟加载3.4.1版本之前默认是true,之后默认是false

测试

mybatis.xml中添加如下配置:
注意:需要放在<properties>标签和<typeAliases>标签之间

<!--全局参数设置-->
<settings>
    <!--延迟加载总开关-->
    <setting name="lazyLoadingEnabled" value="true"/>
    <!--开启侵入式延迟加载-->
    <setting name="aggressiveLazyLoading" value="true"/>
</settings>

测试方法:

@Test
public void selectClassById(int id) {
	MyClass myClass = myClassDao.selectMyClassById(1);
	//触发id为getStudents对应的sql语句
	System.out.println(myClass.getName());
}

//修改aggressiveLazyLoading的值为false,开启深度延迟加载
@Test
public void selectClassById(int id) {
	MyClass myClass = myClassDao.selectMyClassById(1);
	//不会触发id为getStudents对应的sql语句
	System.out.println(myClass.getName());
	//触发id为getStudents对应的sql语句
	System.out.println(myClass.getStudentList().size());
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值