MyBatis 多表关联查询

Posted Java Fans

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MyBatis 多表关联查询相关的知识,希望对你有一定的参考价值。

✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。
🍎个人主页:Java Fans的博客
🍊个人信条:不迁怒,不贰过。小知识,大智慧。
💞当前专栏:SSM 框架从入门到精通
✨特色专栏:国学周更-心性养成之路
🥭本文内容:MyBatis 多表关联查询

文章目录

一对多查询

一对多关联查询是指在查询一方对象的时候,同时将其所关联的多方对象也都查询出来。下面以班级 Classes 与学生 Student 间的一对多关系为例进行演示。一个班级有多个学生,一个学生只属于一个班级。数据库 student 表里面有个字段 classno 是外键,对应主键表 Class 的主键 cid

项目案例:查询班级号为 1801 的班级,同时遍历该班级的所有的学生信息

实现步骤:

【1】在 MySQL 中创建数据库 studentdb,创建表 studentclasses,并添加若干测试用的数据记录,SQL 语句如下:

CREATE DATABASE studentdb;
USE  studentdb ;
DROP TABLE IF EXISTS  student ;
CREATE TABLE  student  (
   id  INT(11) NOT NULL,
   studentname  VARCHAR(20) DEFAULT NULL,
   gender  CHAR(2) DEFAULT NULL,
   age  INT(11) DEFAULT NULL,
   classno VARCHAR(10),
   PRIMARY KEY ( id )
) 
INSERT  INTO  student ( id , studentname , gender , age , classno ) VALUES (1,'张飞','男',18,'201801'),(2,'李白','男',20,'201801'),(3,'张无忌','男',19,'201801'),(4,'赵敏','女',17,'201801');


CREATE TABLE classes (
	cid VARCHAR (30),
	cname VARCHAR (60)
); 
INSERT INTO classes (cid, cname) VALUES('201801','计算机软件1班');
INSERT INTO classes (cid, cname) VALUES('201802','计算机软件2班');

【2】创建实体类 ClassesStudent

Student 类如下:

package cn.kgc.my01.entity;

import lombok.Data;

@Data
public class Student 
    private String sid;
    private String sname;
    private String sex;
    private Integer age;

    //添加额外属性:所在班级
    private  Classes classes;

    public String show()
        return "学生编号:"+getSid()+",学生姓名:"+getSname()+",学生性别:"+getSex()+",学生年龄:"+getAge();
    

Classes 类如下:

package cn.kgc.my01.entity;

import lombok.Data;

import java.util.List;

@Data
public class Classes 
    private String cid;
    private String cname;

    //添加额外属性
    private List<Student> students;

    public String show()
        return "班级编号:"+getCid()+",班级名称:"+getCname()+",班级学生:";
    


【3】创建 ClassesMapper.java 接口,并添加 findClassesById 方法

package cn.kgc.my01.mapper;

import cn.kgc.my01.entity.Classes;

public interface ClassesMapper 
    Classes findClassesById(String id);


【4】创建 ClassesMapper.xml 映射文件,有以下两种方式:

方式一:多表连接查询方式。

这种方式只用到1条 SQL 语句,代码如下所示:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.kgc.my01.mapper.ClassesMapper">
    <!--方式一:多表连接查询方式,只用到 1SQL语句-->
    <resultMap id="classResultMap" type="classes">
        <id property="cid" column="cid"/>
        <result property="cname" column="cname"/>
        <!--关联属性的映射关系-->
        <collection property="students" ofType="Student">
            <id property="sid" column="id"/>
            <result property="sname" column="studentname"/>
            <result property="sex" column="gender"/>
            <result property="age" column="age"/>
        </collection>
    </resultMap>
    <select id="findClassesById" resultMap="classResultMap">
        select cid,cname,id,studentname,gender,age from classes,student
        where classes.cid=student.classno and classes.cid=#cid
    </select>
</mapper>

注意:<resultMap/> 中,如果字段名与属性名相同时,可以在 <resultMap/> 中添加 autoMapping=“true”开启自动映射

另外,在 “一方” 的映射文件中使用 <collection/> 标签体现出两个实体对象间的关联关系。其两个属性的解释如下:

  • property:指定关联属性,即 Class 类中的集合属性 students
  • ofType:集合属性的泛型类型,即 Student

方式二:多表单独查询方式。

多表连接查询方式是将多张表进行连接,连为一张表后进行查询。其查询的本质是一张表。而多表单独查询方式是多张表各自查询各自的相关内容,需要多张表的联合数据,再将主表的查询结果联合其它表的查询结果,封装为一个对象。

多个查询是可以跨越多个映射文件的,即是可以跨越多个namespace 的。在使用其它 namespace 的查询时,添加上其所在的 namespace 即可。这种方式要用到2条 SQL 语句,代码如下所示:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.kgc.my01.mapper.ClassesMapper">
   <!--方式二:多表单独查询方式,也就是分步查询-->
    <resultMap id="classResultMap2" type="classes">
        <id property="cid" column="cid"/>
        <result property="cname" column="cname"/>
        <!--关联属性的映射关系-->
        <collection property="students" ofType="Student">
            <id property="sid" column="id"/>
            <result property="sname" column="studentname"/>
            <result property="sex" column="gender"/>
            <result property="age" column="age"/>
        </collection>
    </resultMap>
    <!-- 以下注释部分属于方式二: 多表单独查询方式 -->
    <resultMap id="studentResultMap" type="student">
        <id property="sid" column="id" />
        <result property="sname" column="studentname" />
        <result property="sex" column="gender" />
        <result property="age" column="age" />
    </resultMap>
    <resultMap id="classesResultMap" type="classes">
        <id property="cid" column="cid" />
        <result property="cname" column="cname" />
    <!-- 关联属性的映射关系 -->
    <!-- 集合的数据来自指定的select查询,该select查询的动态参数来自column指定的字段值 -->
        <collection property="students" ofType="Student" select="selectStudentsByClasses" column="cid"/>
    </resultMap>
    <!-- 多表单独查询,查多方的表 -->
    <select id="selectStudentsByClasses" resultMap="studentResultMap">
        select * from student where calssno=#cid
    </select>

    <!-- 多表单独查询,查一方的表 -->
     <select id="findClassesById" parameterType="String" resultMap="classesResultMap">
        select cid,cname from classes
        where cid=#cid
    </select>
</mapper>


【5】创建 ClassesMapperTest 测试类,并添加如下方法:

package cn.kgc.my01.mapper;

import cn.kgc.my01.entity.Classes;
import cn.kgc.my01.entity.Student;
import junit.framework.TestCase;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class ClassesMapperTest
    SqlSessionFactory factory=null;
    @Before
    public void init()
        try 
            System.out.println("########");
            InputStream resourceAsStream = Resources.getResourceAsStream("config/mybatis-config.xml");

            factory = new SqlSessionFactoryBuilder().build(resourceAsStream);

         catch (IOException e) 
            e.printStackTrace();
        

    
    @Test
    public void testFindClassesById() 
        SqlSession sqlSession = factory.openSession(true);
        ClassesMapper mapper = sqlSession.getMapper(ClassesMapper.class);
        Classes classesById = mapper.findClassesById("201801");
        System.out.println(classesById.show());
        List<Student> students = classesById.getStudents();

        for (Student student : students) 
            System.out.println(student.show());
        
    

方式一:多表连接查询方式测试结果:

DEBUG [main] - ==>  Preparing: select cid,cname,id,studentname,gender,age from 
 classes,student where classes.cid=student.classno and classes.cid=?
DEBUG [main] - ==> Parameters: 201801(String)
DEBUG [main] - <==      Total: 4
班级编号:201801,班级名称:计算机软件1,班级学生:
学生编号:1,学生姓名:张飞,学生性别:男,学生年龄:18
学生编号:2,学生姓名:李白,学生性别:男,学生年龄:20
学生编号:3,学生姓名:张无忌,学生性别:男,学生年龄:19
学生编号:4,学生姓名:赵敏,学生性别:女,学生年龄:17

可以发现,只有一条 SQL 语句,并且是多表联查。

方式二:多表单独查询方式测试结果:

2023-02-15 10:56:49,965 [main] DEBUG DEBUG [main] - ==>  Preparing: select cid,cname from classes where cid=?
DEBUG [main] - ==> Parameters: 201801(String)
DEBUG [main] - ====>  Preparing: select * from student where classno=?
DEBUG [main] - ====> Parameters: 201801(String)
DEBUG [main] - <====      Total: 4
DEBUG [main] - <==      Total: 1
班级编号:201801,班级名称:计算机软件1,班级学生:
学生编号:1,学生姓名:张飞,学生性别:男,学生年龄:18
学生编号:2,学生姓名:李白,学生性别:男,学生年龄:20
学生编号:3,学生姓名:张无忌,学生性别:男,学生年龄:19
学生编号:4,学生姓名:赵敏,学生性别:女,学生年龄:17

可以发现,其 SQL 语句是两条,即各查各的,共用同一个参数。第 1 条先查一方的表,第 2 条再查多方的表。

多对一查询

多对一关联查询是指在查询多方对象的时候,同时将其所关联的一方对象也查询出来。

由于在查询多方对象时也是一个一个查询,所以多对一关联查询,其实就是一对一关联查询。即一对一关联查询的实现方式与多对一的实现方式是相同的。 配置多对一关联的重点在于“多方”的映射文件要有 <association> 属性关联“一方”。

项目案例: 查询学号为1的学生,同时获取他所在班级的完整信息

实现步骤:

【1】创建 StudentMapper.java 接口,并添加方法 searchStudentsById(int id) 如下:

package cn.kgc.my01.mapper;

import cn.kgc.my01.entity.Student;

public interface StudentMapper 
    public Student searchStudentsById(int id);


【2】创建 StudentMapper.xml 映射文件,有以下两种方式:

方式一:多表联合查询。

 <!-- 多表联合查询 -->
    <resultMap id="studentResultMapper" type="student">
        <id property="sid" column="id" />
        <result property="sname" column="studentname" />
        <result property="sex" column="gender" />
        <result property="age" column="age" />
        <!-- 关联属性 -->
        <association property="classes" javaType="classes">
            <id property="cid" column="cid" />
            <result property="cname" column="cname" />
        </association>
    </resultMap>
    <!-- 多表连接查询 -->
    <select id="searchStudentsById" parameterType="int" resultMap="studentResultMapper">
        select cid,cname,id,studentname,gender,age from classes,student
        where classes.cid=student.classno
        and student.id=#id
    </select>

方式二:多表单独查询。

    <!-- 以下注释的是方式二:多表单独查询 -->
    <resultMap id="studentResultMap2" type="student">
        <id property="sid" column="id" />
        <result property="sname" column="studentname" />
        <result property="sex" column="gender" />
        <result property="age" column="age" />	
        <!-- 关联属性 -->
        <association property="classes" javaType="classes" select="findClassesById" column="classno"/>
    </resultMap>
    <select id="searchStudentsById" resultMap="studentResultMap2">
        select id,studentname,gender,age,classno from student where id=#id
    </select>
    <select id="findClassesById" parameterType="String" resultType="classes">
        select cid,cname from classes where cid=#cid
    </select> 

【3】创建 StudentMapperTest 测试类,并添加如下方法:

package cn.kgc.my01.mapper;

import cn.kgc.my01.entity.Classes;
import 以上是关于MyBatis 多表关联查询的主要内容,如果未能解决你的问题,请参考以下文章

Java--Mybatis关联查询,多表同名字段导致SQL报错

MyBatis笔记----多表关联查询

mybatis 关联(多表)查询

MyBatis--多表关联查询

mybatis怎么样使用mapper3实现多表关联查询

Mybatis Plus 多表关联分页查询(亲测有效)