08-使用Mybatis多表关联查询N+1

Posted on 2021-04-18 00:18  萌栈师  阅读(413)  评论(0)    收藏  举报

多表关联查询N+1查询(多对一)

查询所有学生及其对应的班级信息

数据库准备:

一、业务装配实现多表查询(手动)

​ mapper层只做单表查询操作,在service层进行手动装配,实现关联查询的结果

-- sql语句查询
select * from student s join clazz c on s.cid = c.cid;

pojo类

//学生类(多)
public class Student implements Serializable {
  private long sid;
  private String sname;
  private long age;
  private long cid;
  //多对一,多中持有一的对象
  private Clazz clazz;
  
  getter()/setter()
  toString()
}
//班级类(一)
public class Clazz implements Serializable {

  private long cid;
  private String cname;
  private String room;
  
  getter()/setter()
  toString()
}

1.手动在service层装配

1.1 mapper

接口:

public interface StudentMapper {
    //查询学生信息及其所在班级的信息
    public List<Student> getStu();
}

public interface ClazzMapper {
    //查询班级信息
    public Clazz getClazzByCid(int cid);
}

1.2 xml映射文件:

<mapper namespace="com.yd.mapper.StudentMapper">
    <cache></cache>
    <select id="getStu" resultType="Student">
        select * from student
    </select>
</mapper>

<mapper namespace="com.yd.mapper.ClazzMapper">
    <cache></cache>

    <select id="getClazzByCid" resultType="Clazz">
        select * from clazz where cid = #{param1}
    </select>
</mapper>

1.3 运行代码:

@Test
public void getStuWithClz(){
    //查学生信息
    List<Student> students = studentMapper.getStu();
    for (Student s:students) {
        int cid = (int)s.getCid();
        Clazz clazz = clazzMapper.getClazzByCid(cid);
        //手动装配学生类中的Clazz信息
        s.setClazz(clazz);
    }
    System.out.println(students);
    sqlSession.close();
}

查询结果:

[Student{sid=1, sname='老王', age=19, cid=1, clazz=Clazz{cid=1, cname='物理学', room='101'}}, Student{sid=2, sname='静静', age=20, cid=1, clazz=Clazz{cid=1, cname='物理学', room='101'}},....]

二、使用resultMap方式实现N+1

在配置文件中直接完成装配

2.1 mapper接口(不变):

public interface StudentMapper {
    //查询学生信息及其所在班级的信息
    public List<Student> getStu();
}

public interface ClazzMapper {
    //查询班级信息
    public Clazz getClazzByCid(int cid);
}

2.2 xml映射文件

<association>用于关联一个对象

  • property: 指定要关联的属性名

  • select: 设定要继续引用的查询, namespace+id

  • column: 查询时需要传递的列

  • javaType:返回的类型

<mapper namespace="com.yd.mapper.StudentMapper">
    <cache></cache>

    <resultMap id="rm" type="Student">
        <!-- 属性名与数据库列名相同可以省略-->
    <!--<id property="sid" column="sid"></id>-->
    <!--<result property="sname" column="sname"></result>-->
    <!--<result property="age" column="age"></result>-->
    <!--外键要写上,不可以省略-->
    <result property="cid" column="cid"></result>
    <!--装配班级:property在Student类中持有的属性,select:根据哪条查询语句查询 javaType:查询后返回的数据类型 column:根据Student中的哪列查询-->
    <association property="clazz" select="com.yd.mapper.ClazzMapper.getClazzByCid" javaType="Clazz" column="cid">
        <!-- 属性名与数据库列名相同可以省略-->
	<!--<id property="cid" column="cid"></id>-->
	<!--<result property="cname" column="cname"></result>-->
	<!--<result property="room" column="room"></result>-->
    </association>
    </resultMap>
    <select id="getStu" resultMap="rm">
        select * from student
    </select>
</mapper>


<mapper namespace="com.yd.mapper.ClazzMapper">
    <cache></cache>

    <select id="getClazzByCid" resultType="Clazz">
        select * from clazz where cid = #{param1}
    </select>
</mapper>

2.3 运行代码:

由于装配已经完成, service 层只需要调用 mapper 即可, 不需要再进行装配了.

@Test
public void getStuWithClz(){
    List<Student> students = studentMapper.getStu();
    System.out.println(students);
}

查询结果:

[Student{sid=1, sname='老王', age=19, cid=1, clazz=Clazz{cid=1, cname='物理学', room='101'}}, Student{sid=2, sname='静静', age=20, cid=1, clazz=Clazz{cid=1, cname='物理学', room='101'}},....]

三、resultMap的sql关联语句方式实现(N+1)

利用sql的关联查询直接查出所有结果

3.1 mapper接口

  • 不需要关联对象单独查询,一次查所有
public interface StudentMapper {
    //查询学生信息及其所在班级的信息
    public List<Student> getStuBySql();
}

3.2 xml映射文件

  • 在 StudentMapper.xml 中定义多表连接查询 SQL 语句, 一次性查到需要的所有数据, 包括对应班级的信息.

  • 通过<resultMap>定义映射关系, 并通过<association>指定对象属性的映射关系. 可以<association>看成一个

    <resultMap>使用. javaType 属性表示当前对象的类型, 可以写全限定路径或别名.

<resultMap id="rm2" type="Student">
    <id property="sid" column="sid"></id>
    <!-- 属性名与数据库列名的映射关系不能省略-->
    <result property="sname" column="sname"></result>
    <result property="age" column="age"></result>
    <result property="cid" column="cid"></result>
    <association property="clazz" javaType="Clazz">
        <!-- 属性名与数据库列名的映射关系不能省略-->
        <id property="cid" column="cid"></id>
        <result property="cname" column="cname"></result>
        <result property="room" column="room"></result>
    </association>
</resultMap>
<!-- 两种语句均可,第一种可查询时去除同名列,效率比第二种高-->
<select id="getStuBySql" resultMap="rm2">
    select s.sid,s.sname,s.age,s.cid,c.cname,c.room from student s join clazz c on s.cid = c.cid
    <!-- select * from student s join clazz c on s.cid = c.cid -->
</select>

3.3 运行代码:

@Test
public void getStuBySql(){
    List<Student> students = studentMapper.getStuBySql();
    System.out.println(students);
}

查询结果:

四、总结

  • 前两种装配方式中,外键装配的信息需要进行多次查询;
  • 第三种关联查询只要查一次;
  • 前两中装配方式中,xml映射文件中属性和列名相同的可以省略不写;关联查询中,所有列名都必须写上
  • 若属性(列)过多,推荐使用前两种装配方式。

博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3