前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >面试Mybatis之基本操作(collection和association)

面试Mybatis之基本操作(collection和association)

作者头像
小土豆Yuki
发布2020-06-15 17:37:12
4390
发布2020-06-15 17:37:12
举报
文章被收录于专栏:洁癖是一只狗

Mybatis之基本操作

不积跬步,无以至千里;不积小流,无以成江海。

今天我们看一下Mybatis里面的基本操作,一对多,多对一,多对多的使用。

1.一对多场景

学生和教室的关系。一个教室可以有多个学生,如何查询,老套路我们看代码.

第一步实体类

教室实体类

代码语言:javascript
复制
public class ClassRoom {

    private  int id ;

    private  String className;

    private String classAddress;

    private List<Student> studentList;//添加学生list属性
 //get set省略
}

学生实体类

代码语言:javascript
复制
public class Student {

    private int id;

    private String name;

    private boolean pass;
//get set 省略
}

第二步:接口

代码语言:javascript
复制
package com.jiepi.dao;
import java.util.List;

public interface ClassRoomMapper {

    ClassRoom findOneToMany1(int id);//嵌套结果映射:使用嵌套的结果映射来处理连接结果的重复子集

    ClassRoom findOneToMany2(int id);//嵌套 Select 查询:通过执行另外一个 SQL 映射语句来加载期望的复杂类型。
}

第三步:Maper映射文件(有两种实现方式)

第一种实现方式嵌套结果映射

代码语言:javascript
复制
 <resultMap id="oneToManyMap1" type="classroom">
        <id column="cr_id" property="id" />
        <result column="className" property="className"/>
        <result column="classAddress" property="classAddress"/>
        <collection property="studentList" column="cr_id" ofType="student">
            <result column="name" property="name"/>
            <result column="pass" property="pass"/>
        </collection>
    </resultMap>

   <select id="findOneToMany1" resultMap="oneToManyMap1">
       SELECT  cr.id cr_id, className,classAddress ,name,pass from classroom cr inner join student std
               on cr.id=std.classroomId where cr.id=#{id}
   </select>

第二种:实现方式嵌套 Select 查询

代码语言:javascript
复制
  <resultMap id="OneToManyMap2" type="classroom">
        <id column="id" property="id" />
        <result column="className" property="className"/>
        <result column="classAddress" property="classAddress"/>
        <collection property="studentList" column="id" ofType="student" select="findOneToManyChild2">
            <result column="name" property="name"></result>
            <result column="pass" property="pass"/>
        </collection>
    </resultMap>


    <select id="findOneToMany2" resultMap="OneToManyMap2">
        select id,className,classAddress from  classroom where id =#{id}
    </select>

    <select id="findOneToManyChild2" resultType="student">
        select *  from  student where classroomId=#{classroomId}
    </select>

第三步:运行结果

代码语言:javascript
复制
 @Test
    public void find() {
        ClassRoomMapper dao = session.getMapper(ClassRoomMapper.class);
        ClassRoom classRoom = dao.findOneToMany1(1);
        System.out.println(classRoom.toString());
    }

    @Test
    public void find1() {
        ClassRoomMapper dao = session.getMapper(ClassRoomMapper.class);
        ClassRoom classRoom = dao.findOneToMany2(1);
        System.out.println(classRoom.toString());
    }

他们的运行结果是一样的

果然返回一个教室有多个学生

2.多对一场景

多个学生只有一个教室

学生实体类

代码语言:javascript
复制
public class Student {

    private int id;

    private String name;

    private boolean pass;

    private ClassRoom classRoom; //添加房间属性
//get set省略
}

教室实体类

代码语言:javascript
复制
public class ClassRoom {

    private  int id ;

    private  String className;

    private String classAddress;
//get  set  省略
}

第二步:接口

代码语言:javascript
复制
public interface StudentMapper {

    List<Student> findManyToOne1();

    List<Student> findManyToOne2();
}

第三步:配置映射文件

第一种实现方式

代码语言:javascript
复制
<resultMap id="ManyToOneMap1" type="student">
        <id column="id" property="id"/>
        <result column="name" property="name"/>
        <result column="pass" property="pass"/>
        <association property="classRoom" javaType="classroom">
            <result column="className" property="className"/>
            <result column="classAddress" property="classAddress"/>
        </association>
    </resultMap>


    <select id="findManyToOne1" resultMap="ManyToOneMap1">
        SELECT name,pass,className,classAddress
        FROM student stu INNER JOIN classroom cr
        ON stu.classRoomId = cr.id
    </select>

第二种实现方式

代码语言:javascript
复制
 <resultMap id="ManyToOneMap2" type="student">
        <result column="name" property="name"/>
        <result column="pass" property="pass"/>
        <association property="classRoom" javaType="classroom" column="classroomId" select="findManyToOne2Child">
            <result column="className" property="className"/>
            <result column="classAddress" property="classAddress"/>
        </association>
    </resultMap>

    <select id="findManyToOne2" resultMap="ManyToOneMap2">
        SELECT name,pass,classroomId FROM student 
    </select>

    <select id="findManyToOne2Child" resultType="classroom">
        SELECT id,className,classAddress FROM classroom WHERE id = #{id}
    </select>

第四步:测试代码

代码语言:javascript
复制
  @Test
    public void selectList2() throws Exception {
        StudentMapper dao = session.getMapper(StudentMapper.class);
        List<Student> student = dao.findManyToOne2();
        student.stream().forEach((it) -> {
            System.out.println(it);
        });
    }
  @Test
    public void selectList2() throws Exception {
        StudentMapper dao = session.getMapper(StudentMapper.class);
        List<Student> student = dao.findManyToOne1();
        student.stream().forEach((it) -> {
            System.out.println(it);
        });
    }

果然返回多个学生在一个教室

3.多对多场景

一个学生有多个老师,一个老师有多个学生

教师实体类

代码语言:javascript
复制
public class Teacher {

    private int id;

    private String name;

    private List<Student> studentList;//学生类表
}

学生实体类

代码语言:javascript
复制
public class Student {

    private int id;

    private String name;

    private boolean pass;

    private List<Teacher> teacherList;//教师列表

教师实体和学生实体关联实体类

代码语言:javascript
复制
public class TeacherAndStudent {

    private Student student;//学生

    private Teacher teacher;//教师
}

第二步:接口

代码语言:javascript
复制
public interface TeacherAndStudentMapper {

    List<Teacher> findManyToMany();
}

第三步:映射文件

代码语言:javascript
复制
   <resultMap id="ManyToMany" type="teacher">
        <id column="teacherId" property="id"/>
        <result column="teacherName" property="name"/>
        <collection property="studentList" ofType="student" >
            <id column="studentId"  property="id"/>
            <result column="studentName" property="name"/>
            <result column="studentPass" property="pass"/>
        </collection>
    </resultMap>


     <select id="findManyToMany" resultMap="ManyToMany">
         SELECT  t.id teacherId,t.name teacherName,s.id studentId,s.Name studentName,s.pass  studentPass from  teacherAndstudent tas INNER JOIN student s ON s.id=tas.studentId
             INNER JOIN teacher t on t.id=tas.teacherId;
     </select>

第四步:测试结果

代码语言:javascript
复制
    @Test
    public void find() {
        TeacherAndStudentMapper dao = session.getMapper(TeacherAndStudentMapper.class);
        List<Teacher> teacherList = dao.findManyToMany();
        System.out.println(teacherList.toString());
    }

果然返回多个老师,而每个老师有多个学生

我们把级联关系基本操作都已经演示完毕,为了让大家更加深刻,我们在再介绍一下基本的概念。

我们先解释下面的一段代码

代码语言:javascript
复制
  <association property="classRoom" javaType="classroom" column="classroomId" select="findManyToOne2Child">
            <result column="className" property="className"/>
            <result column="classAddress" property="classAddress"/>
  </association>

关联(association)元素处理“有一个”类型的关系。比如,在我们的示例中,一个学生有一个教室。关联结果映射和其它类型的映射工作方式差不多。你需要指定目标属性名以及属性的javaType(很多时候 MyBatis 可以自己推断出来),在必要的情况下你还可以设置 JDBC 类型,如果你想覆盖获取结果值的过程,还可以设置类型处理器。关联的不同之处是,你需要告诉 MyBatis 如何加载关联。MyBatis 有两种不同的方式加载关联:

  1. 嵌套 Select 查询:通过执行另外一个 SQL 映射语句来加载期望的复杂类型。
  2. 嵌套结果映射:使用嵌套的结果映射来处理连接结果的重复子集。

两种不同的方式我们已经演示,但是在我们使用嵌套 Select 查询会存在性能问题。虽然这种方式很简单,但在大型数据集或大型数据表上表现不佳。这个问题被称为“N+1 查询问题”。概括地讲,N+1 查询问题是这样子的:

  1. 你执行了一个单独的 SQL 语句来获取结果的一个列表(就是“+1”)。
  2. 对列表返回的每条记录,你执行一个 select 查询语句来为每条记录加载详细信息(就是“N”)。

这个问题会导致成百上千的 SQL 语句被执行。有时候,我们不希望产生这样的后果。

好消息是,MyBatis 能够对这样的查询进行延迟加载(fetchType,可选的。有效值为 lazy 和 eager),因此可以将大量语句同时运行的开销分散开来(就是先执行主表信息查出来sql语句,如果需要详情信息的时候,再去执行查询详情信息sql语句,分两步执行,有些时候我们只要主表信息,不需要详情信息,就不会执行子查询了)。然而,如果你加载记录列表之后立刻就遍历列表以获取嵌套的数据,就会触发所有的延迟加载查询,性能可能会变得很糟糕。

还有一点,我们必须注意一下,先看一段代码

代码语言:javascript
复制
  <resultMap id="ManyToOneMap1" type="student">
        <id column="id" property="id"/> //这个id元素很重要
        <result column="name" property="name"/>
        <result column="pass" property="pass"/>
        <association property="classRoom" javaType="classroom">
            <result column="className" property="className"/>
            <result column="classAddress" property="classAddress"/>
        </association>
    </resultMap>

非常重要:id 元素在嵌套结果映射中扮演着非常重要的角色。你应该总是指定一个或多个可以唯一标识结果的属性。虽然,即使不指定这个属性,MyBatis 仍然可以工作,但是会产生严重的性能问题。只需要指定可以唯一标识结果的最少属性。显然,你可以选择主键(复合主键也可以)

集合元素(collection)和关联元素几乎是一样的,但是我们注意到一个新的 “ofType” 属性。

这个属性也是很重要的,它用来将 JavaBean(或字段)属性的类型和集合存储的类型区分开来。所以你可以按照下面这样来阅读映射

代码语言:javascript
复制
 <collection property="studentList" column="id" ofType="student" select="findOneToManyChild2">

studentList存储着student类型的ArrayList 集合。

官网提示对关联或集合的映射,并没有深度、广度或组合上的要求。但在映射时要留意性能问题。在探索最佳实践的过程中,应用的单元测试和性能测试会是你的好帮手.

Mybatis的讲解就到这里了,如果有机会我们会对Mybatis进一步讲解,下期我们会对spring进行讲解,欢迎持续关注,抽空还会对面试题进行讲解。有什么面试题不懂的,欢迎在公众号中留言,我会依次为大家总结解答。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-08-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 洁癖是一只狗 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档