07 MyBatis 关联查询(连接查询、分解式查询、延迟加载)

目录

一、MyBatis关联查询

二、MyBatis一对一关联查询

三、MyBatis一对多关联查询

四、MyBatis多对多关联查询

五、MyBatis分解式查询_一对多

以查询班级时关联查询出学生为例,使用N+1(分解式)查询:

六、MyBatis分解式查询_一对一

七、MyBatis分解式查询_延迟加载

1.分解式查询的两种加载方式(立即加载、延迟加载):

2.开启延迟加载:

3.测试延迟加载

 4.什么时候启用延迟加载:


一、MyBatis关联查询

介绍:

MyBatis 的关联查询分为一对一关联查询一对多关联查询
查询对象时,将关联的另一个 对象 查询出来,就是一对一关联查询。
查询对象时,将关联的另一个对象的 集合 查询出来,就是一对多关联查询。
例如有学生类和班级类:
一个学生对应一个班级,也就是学生类中有一个班级属性,这就是一对一关系。
一个班级对应多个学生,也就是班级类中有一个学生集合属性,这就是一对多关系。

1.实体类设计如下:

public class Student {
    /**
     * 主键也可以设置int类型,但是如果是用来接收form表单数据,
     * 就得要设置为Integer,因为form表单传过来的值没有int类型,所以一般主键列设置为Integer类型
     */
    private Integer sid;
    private String name;
    private int age;
    private String sex;
//    一对一关联查询将结果封装到对象中
    private Classes classes;

//  省略getter/setter/toString/构造器
}
public class Classes {
    /**
     * 主键也可以设置int类型,但是如果是用来接收form表单数据,
     * 就得要设置为Integer,因为form表单传过来的值没有int类型,所以一般主键列设置为Integer类型
     */
    private Integer cid;
    private String className;
//    一对多关联查询将结果封装到集合中
    private List<Student> studentList;

//   省略getter/setter/toString/构造器
}

2.数据库设计如下:

二、MyBatis一对一关联查询

介绍:

查询学生时,将关联的一个班级对象查询出来,就是一对一关联查询(一个学生属于一个班级)。

1.创建持久层接口

public interface StudentMapper {
//    1.一对一关联查询
//    一对一关联查询是将结果封装到对象中
    List<Student> findAll();
}

2.创建映射文件

<mapper namespace="com.itbaizhan.mapper.StudentMapper">

    <!--通过resultMap映射字段名与实体类属性名的一一对应关系来解决-->
    <!--id:自定义映射名(自己定义)   type:自定义映射的对象类型-->
    <resultMap id="studentMapper" type="com.itbaizhan.pojo.Student">
        <!--        id:定义主键列   proprty:POJO属性名    column:数据库列名-->
        <id property="sid" column="sid"></id>
        <!--        result:定义普通列   property:POJO属性名    column:数据库列名-->
        <result property="name" column="name"></result>
        <result property="age" column="age"></result>
        <result property="sex" column="sex"></result>

        <!-- 一对一对象列  property:属性名   column:关联列名    javaType:关联对象类型-->
        <association property="classes" column="classId" javaType="com.itbaizhan.pojo.Classes">
            <!--id:定义关联对象主键列   proprty:POJO属性名    column:数据库列名-->
            <id property="cid" column="cid"></id>
            <!--result:定义关联对象普通列   property:POJO属性名    column:数据库列名-->
            <result property="className" column="className"></result>
        </association>
    </resultMap>

    <!--    注意:返回结果是resultMap,不是resultType-->
    <select id="findAll" resultMap="studentMapper">
        select * from student left join classes on student.classId = classes.cid
    </select>
</mapper>

3.在SqlMapConfig配置文件中注册映射文件

<?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.itbaizhan.mapper.StudentMapper">

    <!--通过resultMap映射字段名与实体类属性名的一一对应关系来解决-->
    <!--id:自定义映射名(自己定义)   type:自定义映射的对象类型-->
    <resultMap id="studentMapper" type="com.itbaizhan.pojo.Student">
        <!--        id:定义主键列   proprty:POJO属性名    column:数据库列名-->
        <id property="sid" column="sid"></id>
        <!--        result:定义普通列   property:POJO属性名    column:数据库列名-->
        <result property="name" column="name"></result>
        <result property="age" column="age"></result>
        <result property="sex" column="sex"></result>

        <!-- 一对一对象列(一对一关联查询用association,封装成对象)  property:属性名   column:关联列名    javaType:关联对象类型-->
        <association property="classes" column="classId" javaType="com.itbaizhan.pojo.Classes">
            <!--id:定义关联对象主键列   proprty:POJO属性名    column:数据库列名-->
            <id property="cid" column="cid"></id>
            <!--result:定义关联对象普通列   property:POJO属性名    column:数据库列名-->
            <result property="className" column="className"></result>
        </association>
    </resultMap>

    <!--    注意:返回结果是resultMap,不是resultType-->
    <select id="findAll" resultMap="studentMapper">
        select * from student left join classes on student.classId = classes.cid
    </select>
</mapper>

4.测试一对一关联查询

public class TestManyTableQuery {

    InputStream is = null;
    SqlSession session = null;
    @Before
    public void before() throws IOException {
//        (1)读取核心配置文件
        is = Resources.getResourceAsStream("SqlMapConfig.xml");
//        (2) 创建SqlSessionFactoryBuild对象
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//        (3)SqlSessionFactoryBuilder对象获取SqlSessionFactory对象
        SqlSessionFactory factory = builder.build(is);
//        (4)SqlSessionFactory对象获取SqlSession对象
        session = factory.openSession();
        session.getMapper(StudentMapper.class);
    }

    @After
    public void after() throws IOException {
//        释放资源
        session.close();
        is.close();
    }

//    一对一关联查询,实体类:Student
    @Test
    public void testFindAllStudent(){
        StudentMapper studentMapper = session.getMapper(StudentMapper.class);
        List<Student> all = studentMapper.findAll();
        all.forEach(System.out::println);
    }
}

三、MyBatis一对多关联查询

介绍:

查询班级时,将关联的学生集合查询出来,就是一对多关联查询(一个班级对应多个学生)。

1.创建持久层接口

public interface ClassesMapper {
//    2.一对多关联查询
//      一对多关联查询将结果封装到集合中
    List<Classes> findAll();
}

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.itbaizhan.mapper.ClassesMapper">

    <!--通过resultMap映射字段名与实体类属性名的一一对应关系来解决-->
    <!--id:自定义映射名(自己定义)   type:自定义映射的对象类型-->
    <resultMap id="classesMapper" type="com.itbaizhan.pojo.Classes">
        <!--        id:定义主键列   proprty:POJO属性名    column:数据库列名-->
        <id property="cid" column="cid"></id>
        <!--        result:定义普通列   property:POJO属性名    column:数据库列名-->
        <result property="className" column="className"></result>

        <!-- 一对多对象列(一对多关联查询用collection,封装成集合)  property:属性名   column:关联列名    ofType:关联对象类型-->
        <collection property="studentList" column="classId" ofType="com.itbaizhan.pojo.Student">
            <!--        id:定义主键列   proprty:POJO属性名    column:数据库列名-->
            <id property="sid" column="sid"></id>
            <!--        result:定义普通列   property:POJO属性名    column:数据库列名-->
            <result property="name" column="name"></result>
            <result property="age" column="age"></result>
            <result property="sex" column="sex"></result>
        </collection>
    </resultMap>

    <!--    注意:返回结果是resultMap,不是resultType-->
    <select id="findAll" resultMap="classesMapper">
        select * from classes left join student  on classes.cid = student.classId;   
    </select>
</mapper>

3.测试一对多关联查询

//      一对多关联查询,实体类:Classes
    @Test
    public void testFindAllClasses(){
        ClassesMapper classesMapper = session.getMapper(ClassesMapper.class);
        List<Classes> all = classesMapper.findAll();
        all.forEach(System.out::println);
    }

四、MyBatis多对多关联查询

 介绍:

MyBatis 多对多关联查询本质就是两个一对多关联查询。
例如有老师类和班级类:
一个老师对应多个班级,也就是老师类中有一个班级集合属性。
一个班级对应多个老师,也就是班级类中有一个老师集合属性。

1.实体类设计如下:

public class Classes {
    /**
     * 主键也可以设置int类型,但是如果是用来接收form表单数据,
     * 就得要设置为Integer,因为form表单传过来的值没有int类型,所以一般主键列设置为Integer类型
     */
    private Integer cid;
    private String className;
//    一对多关联查询将结果封装到集合中
    private List<Student> studentList;
    private List<Teacher> teacherList;

//   省略getter/setter/toString/构造器
}
public class Teacher {
    /**
     * 主键也可以设置int类型,但是如果是用来接收form表单数据,
     * 就得要设置为Integer,因为form表单传过来的值没有int类型,所以一般主键列设置为Integer类型
     */
    private Integer tid;
    private String tname;
    private List<Classes> classesList;

//    省略getter/setter/toString/构造器
}

 2.数据库设计:

在数据库设计中,需要建立中间表,双方与中间表均为一对多关系。

3.创建持久层接口

public interface TeacherMapper {
//    多对多关联查询:相当于两个一对多关联查询
    List<Teacher> findAll();
}

4.创建映射文件

<?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.itbaizhan.mapper.TeacherMapper">

    <!--通过resultMap映射字段名与实体类属性名的一一对应关系来解决-->
    <!--id:自定义映射名(自己定义)   type:自定义映射的对象类型-->
    <resultMap id="teacherMapper" type="com.itbaizhan.pojo.Teacher">
        <!--        id:定义主键列   proprty:POJO属性名    column:数据库列名-->
        <id property="tid" column="tid"></id>
        <!--        result:定义普通列   property:POJO属性名    column:数据库列名-->
        <result property="tname" column="tname"></result>

        <!-- 多对多对象列(多对多关联查询用collection,封装成集合)  property:属性名   column:关联列主键名    ofType:关联对象类型-->
        <collection property="classesList" column="cid" ofType="com.itbaizhan.pojo.Classes">
            <!--        id:定义主键列   proprty:POJO属性名    column:数据库列名-->
            <id property="cid" column="cid"></id>
            <!--        result:定义普通列   property:POJO属性名    column:数据库列名-->
            <result property="className" column="className"></result>
        </collection>
    </resultMap>

    <!--    注意:返回结果是resultMap,不是resultType-->
    <select id="findAll" resultMap="teacherMapper">
        select * from teacher
            left join classes_teacher
                on teacher.tid = classes_teacher.tid
            left join classes
                on classes_teacher.cid = classes.cid;
    </select>
</mapper>

5.测试多对多关联查询

//      多对多关联查询,实体类:Teacher
    @Test
    public void testFindAllTeacher(){
        TeacherMapper teacherMapper = session.getMapper(TeacherMapper.class);
        List<Teacher> all = teacherMapper.findAll();
        all.forEach(System.out::println);
    }

五、MyBatis分解式查询_一对多

1.介绍:

 在MyBatis多表查询中,使用连接查询时一个Sql语句就可以查询出所有的数据。如:

//查询班级时关联查询出学生
 select * from classes left join student  on classes.cid = student.classId;
也可以使用分解式查询,即将一个连接 Sql 语句分解为多条 Sql 语句,也叫N+1查询;如:
//查询班级时关联查询出学生
select *from classes;
select *from student where classId = 1;
select *from student where classId = 2;

2.连接查询、分解式查询优缺点

连接查询:
优点:降低查询次数,从而提高查询效率。
缺点:如果查询返回的结果集较多会消耗内存空间。
N+1 查询:
优点:结果集分步获取,节省内存空间。
缺点:由于需要执行多次查询,相比连接查询效率低。

以查询班级时关联查询出学生为例,使用N+1(分解式)查询:

1.创建每个查询语句的持久层接口方法

public interface ClassesMapper2 {
//     分解式查询_一对多
//     查询所有班级并关联查出班级对应的学生
    List<Classes> findAll();
}
public interface StudentMapper2 {
//      根据班级Id查询所有学生
    List<Student> findByClassId(int classId);
}

2.在映射文件中进行配置

//ClassesMapper2.xml
<select id="findAll" resultType="com.itbaizhan.pojo.Classes">
   select * from classes
</select> 
//StudentMapper2.xml
<select id="findByClassId" resultType="com.itbaizhan.pojo.Student" parameterType="int">
   select * from student where classId = ${classId}
</select>

3.修改主表映射文件中的查询方法

N+1查询与连接查询相比,需要在 <collection> <association> 中添加属性<select>

//ClassesMapper2.xml
<mapper namespace="com.itbaizhan.mapper2.ClassesMapper2">

      <!--1.分解式查询_一对多
        先查询出所有班级,然后再查询出每个班级对应的学生,所以用<resultMap>映射一一对应关系-->
    <!--自定义映射关系-->
    <resultMap id="MyClassesMapper" type="com.itbaizhan.pojo.Classes">
        <id property="cid" column="cid"></id>
        <result property="className" column="className"></result>

<!--property:属性名  ofType:属性的泛型  select:从表查询调用的方法  column:调用方法时传入的参数字段-->
    <!--注意:1.分解式查询要用select调用从表(Student表)中的方法才能引入
             2.要用column引入从表中的主键,因为通过从表主键来查询到从表的数据-->
        <collection property="studentList"
                    ofType="com.itbaizhan.pojo.Student"
                    select="com.itbaizhan.mapper2.StudentMapper2.findByClassId"
                    column="cid">
        </collection>
    </resultMap>
</mapper>

4.测试查询方法

//    分解式查询_一对多
    @Test
    public void testFindAllClasses2(){
        ClassesMapper2 classesMapper2 = session.getMapper(ClassesMapper2.class);
        List<Classes> all = classesMapper2.findAll();
        all.forEach(System.out::println);
    }

5.查询结果

 我们可以看到在控制台打印出了多条Sql语句,表示执行了多条sql语句;

六、MyBatis分解式查询_一对一

介绍:

查询学生时关联查询出班级也可以使用分解式查询,首先将查询语句分开:

select * from student;
select * from classes where cid = ?

1.创建每个查询语句的持久层方法

//接口ClassesMapper2
//    根据Id查询班级
    Classes findByCid(int cid);
//接口StudentMapper2
//       分解式查询_一对一
//       查询所有学生并关联查出学生对应的班级
    List<Student> findAll();

 2.在映射文件中进行配置

//ClassesMapper2.xml  
 <!--分解式查询_一对一-->
    <select id="findByCid" resultType="com.itbaizhan.pojo.Classes" parameterType="int">
        select * from classes where cid = ${cid}
    </select>
//StudentMapper2.xml
  <select id="findAll" resultType="com.itbaizhan.pojo.Studnet">
        select * from student
    </select>

3.修改主表映射文件中的查询方法

N+1查询与连接查询相比,需要在 <collection> <association> 中添加属性<select>

//StudentMapper2.xml
<!--分解式查询_一对一-->
    <resultMap id="MyStudentMapper" type="com.itbaizhan.pojo.Student">
        <id property="sid" column="sid"></id>
        <result property="name" column="name"></result>
        <result property="age" column="age"></result>
        <result property="sex" column="sex"></result>

        <association property="classes"
                     javaType="com.itbaizhan.pojo.Classes"
                     select="com.itbaizhan.mapper2.ClassesMapper2.findByCid"
                     column="classId">
        </association>
    </resultMap>

    <select id="findAll" resultMap="MyStudentMapper">
        select * from student
    </select>

4.测试查询方法

//    分解式查询_一对一
    @Test
    public void testFindAllStudent2(){
        StudentMapper2 studentMapper2 = session.getMapper(StudentMapper2.class);
        List<Student> all = studentMapper2.findAll();
        all.forEach(System.out::println);
    }

5.查询结果

七、MyBatis分解式查询_延迟加载

1.分解式查询的两种加载方式(立即加载、延迟加载):

立即加载:在查询主表时就执行所有的 Sql 语句。
延迟加载:又叫懒加载,首先执行主表的查询语句,使用从表数据时才触发从表的查询语句。 延迟加载在获取关联数据时速度较慢,但可以节约资源,即用即取。

2.开启延迟加载:

(1)在SqlMapConfig.xml配置文件中开启:
此方式为: 设置所有的N+1 查询都为延迟加载:
 <settings>
    <!--此方式为:设置所有的分解式(N+1)查询都为延迟加载:
        注意:这种方式很少使用,因为不可能为所以分解式查询都设置为延迟加载-->
        <setting name="lazyLoadingEnabled" value="true"/>
    </settings>
(2)设置某个方法为延迟加载:
<association> 或   <collection> 中添加 fetchType 属性设置加载方式。
          lazy :延迟加载;        eager :立即加载。
    <!--为一对多分解式查询加入延迟加载
        fetchType:加载类型    lazy:懒(延迟)加载    eager:立即加载-->
    <resultMap id="MyClassesMapper" type="com.itbaizhan.pojo.Classes">
        <id property="cid" column="cid"></id>
        <result property="className" column="className"></result>
        <collection property="studentList"
                    ofType="com.itbaizhan.pojo.Student"
                    select="com.itbaizhan.mapper2.StudentMapper2.findByClassId"
                    column="cid"
                    fetchType="lazy">
        </collection>
    </resultMap>

    <select id="findAll" resultMap="MyClassesMapper">
        select * from classes
    </select>

3.测试延迟加载

(1)测试延迟加载

    // 分解式查询_一对多:测试延迟加载
    @Test
    public void testFindAllClasses3(){
        ClassesMapper2 classesMapper2 = session.getMapper(ClassesMapper2.class);
        List<Classes> all = classesMapper2.findAll();
        all.forEach(System.out::println);

        System.out.println("------------------------------");
        System.out.println(all.get(0).getStudentList());
    }

(2)测试结果

由于打印对象时会调用对象的 toString 方法, toString 方法默认会触发延迟加载的查询,所以我们无法测试出延迟加载的效果。

(3)在配置文件中设置lazyLoadTriggerMethods属性

我们在配置文件中设置lazyLoadTriggerMethods属性,该属性可以指定对象的什么方法触发延迟加载,设置为空字符串即可

<!--此方式为:映射文件中<collection>或<association>中设置了fetchType的方法触发延迟加载,设置为空字符串即可。-->
    <settings>
        <setting name="lazyLoadTriggerMethods" value=""/>
    </settings>

(4)再次测试延迟加载并显示结果:

 4.什么时候启用延迟加载:

一般情况下,一对多查询使用延迟加载一对一查询使用立即加载。因为一对多可能有上万甚至更多的数据,如果立即加载的话会很占用空间,所以使用延迟加载;而一对一查询的话,只有一条数据,立即加载也不会有什么影响,所以可以立即加载。

5.知识点整理:

(1)在配置文件的 <setting>中, name 值配置成“lazyLoadingEnabled” ,这种情况下所有的N+1(分解式)查询都是懒加载。

(2)在 <association> <collection> 中添加<fetchType>可以设置加载方式,

                        lazy:懒加载      eager:立即加载。

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Mybatis是一种Java持久层框架,它提供了强大的关联查询功能。根据表与表间的关联关系的不同,关联查询分为四种:一对一关联查询、一对多关联查询、多对一关联查询和多对多关联查询[^1]。 一对一关联查询是指两个表之间存在一对一的关系,可以通过在查询语句中使用嵌套查询或者使用关联映射来实现。嵌套查询是指在主查询中嵌套子查询,通过子查询获取关联表的数据。关联映射是指在主查询的结果集中包含关联表的数据,通过配置关联映射来实现。以下是一个示例代码演示了如何在Mybatis中进行一对一关联查询: ```xml <!-- 定义关联映射 --> <resultMap id="userMap" type="User"> <id property="id" column="id"/> <result property="name" column="name"/> <result property="address" column="address"/> <association property="card" javaType="Card"> <id property="id" column="card_id"/> <result property="cardNo" column="card_no"/> </association> </resultMap> <!-- 执行关联查询 --> <select id="getUserWithCard" resultMap="userMap"> SELECT u.id, u.name, u.address, c.id as card_id, c.card_no FROM user u INNER JOIN card c ON u.card_id = c.id WHERE u.id = #{id} </select> ``` 以上代码中,定义了一个关联映射`userMap`,其中包含了`User`和`Card`两个实体类的属性映射关系。在执行关联查询时,通过`INNER JOIN`将`user`表和`card`表关联起来,并通过`WHERE`条件限定查询结果。最终将查询结果映射到`User`对象中,其中`User`对象中的`card`属性也会被自动填充。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值