MyBatis 多表关联查询

news2024/10/2 20:33:00

在这里插入图片描述

✅作者简介: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 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;

public class StudentMapperTest {
    SqlSessionFactory factory=null;
    @Before
    public void init(){
        try {
            InputStream resourceAsStream = Resources.getResourceAsStream("config/mybatis-config.xml");

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

        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    @Test
    public void testSearchStudentsById() {
        SqlSession sqlSession = factory.openSession(true);
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        Student student = mapper.searchStudentsById(1);
        System.out.println(student.show());

        System.out.println("所在班级:");
        Classes classes=student.getClasses();
        System.out.println(classes.toString());
    }
}

方式一:多表联合查询方式测试结果:

DEBUG [main] - ==>  Preparing: select cid,cname,id,studentname,gender,age from 
     classes,student where classes.cid=student.classno and student.id=?
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <==      Total: 1
学生编号:1,学生姓名:张飞,学生性别:男,学生年龄:18
所在班级:
Classes(cid=201801, cname=计算机软件1, students=null)

可以发现,它发出的 SQL 语句是多表查询。

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

DEBUG [main] - ==>  Preparing: select id,studentname,gender,age,classno from student where id=?
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - ====>  Preparing: select cid,cname from classes where cid=?
DEBUG [main] - ====> Parameters: 201801(String)
DEBUG [main] - <====      Total: 1
DEBUG [main] - <==      Total: 1
学生编号:1,学生姓名:张飞,学生性别:男,学生年龄:18
所在班级:
Classes(cid=201801, cname=计算机软件1, students=null)

可以发现,它发出的 SQL 语句是两条,即各查各的,共用同一个参数。

自连接查询

自连接的查询可以用一对多来处理,也可以用多对一来处理。例如,员工表,每个员工都有一个上司,但上司同时也是员工表的一条记录,这种情况可用自连接查询出每个员工对应的上司信息,也可以查出每个上司有哪些下属员工。

使用多对一的方式实现自连接

项目案例:查询员工的信息及对应的上司信息。

思路分析: 可将员工当做多方,上司当做一方。

实现步骤:

【1】修改数据库。

添加一个表 employee 并插入测试数据,具体如下:

create table employee (
	empid double ,
	empname varchar (60),
	job varchar (60),
	leader double 
); 
insert into employee (empid, empname, job, leader) values('1','jack','clerk','3');
insert into employee (empid, empname, job, leader) values('2','mike','salesman','3');
insert into employee (empid, empname, job, leader) values('3','john','manager','4');
insert into employee (empid, empname, job, leader) values('4','smith','president',NULL);
insert into employee (empid, empname, job, leader) values('5','rose','salesman','3');

【2】创建实体类 Employee,代码如下:

package cn.kgc.my01.entity;

public class Employee {
	private int empid;
	private String empname;
	private String job;
	private Employee leader;
	public int getEmpid() {
		return empid;
	}
	public void setEmpid(int empid) {
		this.empid = empid;
	}
	public String getEmpname() {
		return empname;
	}
	public void setEmpname(String empname) {
		this.empname = empname;
	}
	public String getJob() {
		return job;
	}
	public void setJob(String job) {
		this.job = job;
	}
	public Employee getLeader() {
		return leader;
	}
	public void setLeader(Employee leader) {
		this.leader = leader;
	}	
	public String toString(){
		return "员工编号:"+getEmpid()+",员工姓名:"+getEmpname()+",员工职位:"+getJob();
	}
}

可以发现,里面存在着嵌套,Employee 里面的一个属性 leader 本身就是 Employee 类型。

【3】创建 EmployeeMapper.java 接口,添加 findEmployeeAndLeaderById 方法如下:

package cn.kgc.my01.mapper;

import cn.kgc.my01.entity.Employee;

public interface EmployeeMapper {
    Employee findEmployeeAndLeaderById(int id);
}

【4】创建 EmployeeMapper.xml 映射文件,代码如下:

<?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.EmployeeMapper">

    <resultMap id="empResultMap" type="employee">
        <id property="empid" column="empid" />
        <result property="empname" column="empname" />
        <result property="job" column="job" />
        <association property="leader" javaType="Employee"
                     select="findEmployeeAndLeaderById" column="leader"/>
    </resultMap>
    <select id="findEmployeeAndLeaderById" parameterType="int" resultMap="empResultMap">
        select * from employee where empid=#{empid}
    </select>
</mapper>

【5】创建 EmployeeMapperTest.java 测试类

package cn.kgc.my01.mapper;

import cn.kgc.my01.entity.Classes;
import cn.kgc.my01.entity.Employee;
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.Scanner;

public class EmployeeMapperTest {
    SqlSessionFactory factory=null;
    @Before
    public void init(){
        try {
            InputStream resourceAsStream = Resources.getResourceAsStream("config/mybatis-config.xml");

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

        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    @Test
    public void testFindEmployeeAndLeaderById() {
        SqlSession sqlSession = factory.openSession(true);
        EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
        Employee employee=mapper.findEmployeeAndLeaderById(1);
        Employee leader=employee.getLeader();
        System.out.println(employee.toString());
        System.out.println("他的上司是:"+leader.toString());
        //System.out.println("他的上司的上司是:"+leader.getLeader().toString());
    }
}

测试结果: 查询员工

DEBUG [main] - ==>  Preparing: select * from employee where empid=?
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - ====>  Preparing: select * from employee where empid=?
DEBUG [main] - ====> Parameters: 3(Integer)
DEBUG [main] - ======>  Preparing: select * from employee where empid=?
DEBUG [main] - ======> Parameters: 4(Integer)
DEBUG [main] - <======      Total: 1
DEBUG [main] - <====      Total: 1
DEBUG [main] - <==      Total: 1
员工编号:1,员工姓名:jack,员工职位:clerk
他的上司是:员工编号:3,员工姓名:john,员工职位:manager

从上面的 SQL 语句中发现,出现了 3 条 SQL 语句,这个查询存在嵌套,先查员工1,然后查他的直接上司3,再查上司的上司4。这种情况不影响什么,甚至可以实现直接输出上司的上司,但要注意输出语句不要出现地柜,即输出语句不要出现输出上司。

要同时查上司的上司,只需要在上面的测试类中多加一条语句:

System.out.println("他的上司的上司是:"+leader.getLeader().toString());

使用一对多的方式实现自连接

项目案例:查询某位领导及其直接下属员工。

思路分析: 可用一对多的方式来实现,员工(领导)当作一方,员工(下属)当作多方。

实现步骤:

【1】修改实体类 Employee,代码如下:

package cn.kgc.my01.entity;

import java.util.List;

public class Employee {
	private int empid;
	private String empname;
	private String job;
	//员工的上司
	private Employee leader;

	//员工的下属
	private List<Employee> employees;
	public List<Employee> getEmployees() {
		return employees;
	}
	public void setEmployees(List<Employee> employees) {
		this.employees = employees;
	}
	public int getEmpid() {
		return empid;
	}
	public void setEmpid(int empid) {
		this.empid = empid;
	}
	public String getEmpname() {
		return empname;
	}
	public void setEmpname(String empname) {
		this.empname = empname;
	}
	public String getJob() {
		return job;
	}
	public void setJob(String job) {
		this.job = job;
	}
	public Employee getLeader() {
		return leader;
	}
	public void setLeader(Employee leader) {
		this.leader = leader;
	}
	public String toString(){
		return "员工编号:"+getEmpid()+",员工姓名:"+getEmpname()+",员工职位:"+getJob();
	}
}

【2】在 EmployeeMapper.java 接口中,添加 findLeaderAndEmployeesById 方法如下:

Employee findLeaderAndEmployeesById(int id);

【3】在 EmployeeMapper.xml 映射文件中,添加 findEmployeeAndLeaderById 的映射方法内容如下:

    <!-- 一对多的方式实现自连接 -->
    <resultMap id="empResultMap2" type="employee">
        <id property="empid" column="empid" />
        <result property="empname" column="empname" />
        <result property="job" column="job" />
        <!-- 关联属性的映射关系
        集合的数据来自指定的select查询,该select查询的动态参数来自column指定的字段值 -->
        <collection property="employees" ofType="employee"
                    select="selectEmployeesByLeader" column="empid"/>
    </resultMap>
    <select id="selectEmployeesByLeader" resultType="employee">
        select * from employee where leader=#{empid}
    </select>
    <select id="findLeaderAndEmployeesById" parameterType="int" resultMap="empResultMap2">
        select * from employee where empid=#{empid}
    </select>

【4】在 EmployeeMapperTest.java 测试类中,添加如下内容:

    @Test
    public void testTestFindLeaderAndEmployeesById() {
        SqlSession sqlSession = factory.openSession(true);
        EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
        Employee leader=mapper.findLeaderAndEmployeesById(4);
        List<Employee> employees = leader.getEmployees();
        System.out.println(leader.toString());
        System.out.println("他的直接下属有:");
        for (Employee employee : employees) {
            System.out.println(employee.toString());
        }
    }

测试结果: 查询经理

DEBUG [main] - ==>  Preparing: select * from employee where empid=?
DEBUG [main] - ==> Parameters: 4(Integer)
DEBUG [main] - ====>  Preparing: select * from employee where leader=?
DEBUG [main] - ====> Parameters: 4.0(Double)
DEBUG [main] - <====      Total: 1
DEBUG [main] - <==      Total: 1
员工编号:4,员工姓名:smith,员工职位:president
他的直接下属有:
员工编号:3,员工姓名:john,员工职位:manager

多对多查询

原理: 多对多可以分拆成两个一对多来处理,需要一个中间表,各自与中间表实现一对多的关系。

项目案例:一个学生可以选人修多门课程,一门课程可以给多个学生选修,课程与学生之间是典型的多对多。实现查询一个学生信息,同时查出他的所有选修课,还有实现查询一门课程信息,同时查出所有的选修了该课程的学生信息。

思路分析: 多对多需要第三表来体现,数据库中除了课程表,学生表,还需要学生课程表。

实现步骤:

【1】修改数据库,代码如下:

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班');

CREATE TABLE employee (
	empid DOUBLE ,
	empname VARCHAR (60),
	job VARCHAR (60),
	leader DOUBLE 
); 
INSERT INTO employee (empid, empname, job, leader) VALUES('1','jack','clerk','3');
INSERT INTO employee (empid, empname, job, leader) VALUES('2','mike','salesman','3');
INSERT INTO employee (empid, empname, job, leader) VALUES('3','john','manager','4');
INSERT INTO employee (empid, empname, job, leader) VALUES('4','smith','president',NULL);
INSERT INTO employee (empid, empname, job, leader) VALUES('5','rose','salesman','3');


CREATE TABLE course (
	courseid DOUBLE ,
	coursename VARCHAR (90)
); 
INSERT INTO course (courseid, coursename) VALUES('1','java');
INSERT INTO course (courseid, coursename) VALUES('2','android');
INSERT INTO course (courseid, coursename) VALUES('3','PHP');

CREATE TABLE studentcourse (
	id DOUBLE ,
	studentid DOUBLE ,
	courseid DOUBLE 
); 
INSERT INTO studentcourse (id, studentid, courseid) VALUES('1','1','1');
INSERT INTO studentcourse (id, studentid, courseid) VALUES('2','1','2');
INSERT INTO studentcourse (id, studentid, courseid) VALUES('3','2','1');
INSERT INTO studentcourse (id, studentid, courseid) VALUES('4','2','2');
INSERT INTO studentcourse (id, studentid, courseid) VALUES('5','3','1');
INSERT INTO studentcourse (id, studentid, courseid) VALUES('6','3','2');
INSERT INTO studentcourse (id, studentid, courseid) VALUES('7','1','3');

【2】新增实体类 Course 和修改实体类 Student 。

Course 类如下:

package cn.kgc.my01.entity;

import lombok.Data;

import java.util.List;

@Data
public class Course {
    private int courseid;
    private String coursename;
    private List<Student> students;

    public String toString(){
        return "课程编号:"+getCourseid()+",课程名称:"+getCoursename();
    }
}

Student类如下,添加一个属性courses和getter,setter方法。

    private List<Course> courses;
    public List<Course> getCourses() {
        return courses;
    }
    public void setCourses(List<Course> courses) {
        this.courses = courses;
    }

【3】新建 StudentMapper.java 接口,并添加一个方法如下:

package cn.kgc.my01.mapper;

import cn.kgc.my01.entity.Student;

public interface StudentMapper {
    public Student searchStudentById(int id);
}

【4】配置对应的 StudentMapper.xml 映射,代码如下:

<?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.StudentMapper">
    <resultMap id="studentMap2" type="student">
        <id property="sid" column="id" />
        <result property="sname" column="studentname" />
        <result property="sex" column="gender" />
        <result property="age" column="age" />

        <!-- 关联属性的映射关系 -->
        <collection property="courses" ofType="Course">
            <id property="courseid" column="courseid" />
            <result property="coursename" column="coursename" />
        </collection>
    </resultMap>
    <!-- 多表连接查询 -->
    <select id="searchStudentById" parameterType="int" resultMap="studentMap2">
        select student.id,studentname,gender,age,course.courseid,coursename from course,student,studentcourse
        where course.courseid=studentcourse.courseid
        and student.id=studentcourse.studentid and student.id=#{id}
    </select>
</mapper>

【5】创建测试类 StudentMapperTest 类

package cn.kgc.my01.mapper;

import cn.kgc.my01.entity.Classes;
import cn.kgc.my01.entity.Course;
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 StudentMapperTest {
    SqlSessionFactory factory=null;
    @Before
    public void init(){
        try {
            InputStream resourceAsStream = Resources.getResourceAsStream("config/mybatis-config.xml");

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

        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    @Test
    public void testSearchStudentsById() {
        SqlSession sqlSession = factory.openSession(true);
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        Student student = mapper.searchStudentById(1);
        System.out.println(student.show());

        System.out.println("-----该生选修了以下课程:-----------");
        List<Course> courses=student.getCourses();
        for(Course course:courses){
            System.out.println(course.toString());
        }
    }


}

测试结果:

在这里插入图片描述

【6】新建 CourseMapper.java 接口,并添加一个方法如下:

package cn.kgc.my01.mapper;

import cn.kgc.my01.entity.Course;

public interface CourseMapper {
    //根据id查找课程,即时获取选个性该课程的学生
    public Course searchCourseById(int id);
}

【7】配置对应的 CourseMapper.xml 映射,代码如下:

<?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.CourseMapper">
  <resultMap id="courseMap" type="course">
    <id property="courseid" column="courseid" />
    <result property="coursename" column="coursename" />
    <!-- 关联属性的映射关系 -->
    <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="searchCourseById" parameterType="int" resultMap="courseMap">
    select student.id,studentname,gender,age,course.courseid,coursename from course,student,studentcourse
    where course.courseid=studentcourse.courseid
    and student.id=studentcourse.studentid and course.courseid=#{courseid}
  </select>
</mapper>

【8】创建测试类 CourseMapperTest 类

package cn.kgc.my01.mapper;

import cn.kgc.my01.entity.Course;
import cn.kgc.my01.entity.Employee;
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 CourseMapperTest{
    SqlSessionFactory factory=null;
    @Before
    public void init(){
        try {
            InputStream resourceAsStream = Resources.getResourceAsStream("config/mybatis-config.xml");

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

        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    @Test
    public void testSearchCourseById() {
        SqlSession sqlSession = factory.openSession(true);
        CourseMapper mapper = sqlSession.getMapper(CourseMapper.class);
        Course course = mapper.searchCourseById(1);
        System.out.println(course.toString());
        System.out.println("-------该课程有以下学生选修:------");
        List<Student> students=course.getStudents();
        for(Student student:students){
            System.out.println(student.show());
        }
    }
}

测试效果:

在这里插入图片描述


  码文不易,本篇文章就介绍到这里,如果想要学习更多Java系列知识点击关注博主,博主带你零基础学习Java知识。与此同时,对于日常生活有困扰的朋友,欢迎阅读我的第四栏目:《国学周更—心性养成之路》,学习技术的同时,我们也注重了心性的养成。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/347896.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【测试工程师面试】详细记录 自己的一次面试

【测试工程师面试】详细记录 自己的一次面试 目录&#xff1a;导读 Linux基础 Oracle基础 编程基础 测试的基础 面试的问题 扯闲话部分&#xff1a; 10点刚到&#xff0c;先进行笔试&#xff0c;笔试的题目很基础&#xff0c;涉及到linux&#xff0c;涉及到oracle数据库…

【python百炼成魔】python运算符的使用与输入输出函数

文章目录前言一. python 运算符1.1 算术运算符1.2 .赋值运算符1.3 比较运算符1.4. 布尔运算符二. 输入和输出函数2.1 print函数2.1.1 help函数查看帮助文档2.1.2 print的格式化输出2.2 format函数2.3 input数据接收函数写在最后前言 Python 中的运算符主要分为算术运算符、比较…

HydroD 实用教程(三)环境数据

目 录一、前言二、Location三、Wind Profile四、Directions五、Water5.1 Wave Spectrums5.2 Current Profile5.3 Frequency Set5.4 Phase Set5.5 Wave Height5.6 Regular Wave Set六、参考文献一、前言 SESAM &#xff08;Super Element Structure Analysis Module&#xff09;…

MySQL获取当前时间的各种方式

1 获取当前完整时间1.1 now()函数select now();输出:2023-02-15 10:46:171.2 sysdate()函数select sysdate();输出:2023-02-15 10:47:131.3 current_timestamp或current_timestamp()current_timestamp和current_timestamp()函数的效果是一样的&#xff0c;只不过一个是关键字&a…

“深度学习”学习日记。--ImageNet、VGG、ResNet

2023.2.14 一、小历史&#xff1a; 在2012年的ILSVRC(ImageNet Large Scale Visual Recognitoin Chanllege)&#xff0c;基于深度学习的方法AlexNet 以绝对优势获胜并且他颠覆了以前的图片识别方法&#xff0c;此后深度学习方法一直活跃在这个舞台。 二、ImageNet&#xff1…

10款让程序员用了会上瘾的工具

程序员必定会爱上的10款工具&#xff0c;相信肯定有不少是你的心头好&#xff0c;尤其是第4款。 第一款&#xff1a;TrueCryptTrueCrypt 可能很多人没用过&#xff0c;它是一个加密软件&#xff0c;能够对磁盘进行加密。还在担心自己电脑中的重要文件、私密档案被人查看&…

密码技术在车联网安全中的应用与挑战

随着智慧交通和无人驾驶的快速发展&#xff0c;车联网产业呈现蓬勃发展态势&#xff0c;车与云、车与车、车与路、车与人等综合网络链接的融合程度越来越高&#xff0c;随之而来的安全挑战也更加严峻。解决车联网的安全问题需要一个整体的防护体系&#xff0c;而密码技术凭借技…

深度理解机器学习17-候选激活函数

深度理解机器学习17-候选激活函数 ​ 替换先前时间步长激活函数的候选激活函数&#xff0c;也在每个时间步长中计算。顾名思义&#xff0c;候选激活函数代表下一个时间步长激活函数应该采用的替代值。 ​ 计算候选激活函数的表达式&#xff1a; ​ 从数学上讲&#xff0c;更…

chatgpt系列文章-23.2.15(主要还在发现chatgpt的不足,偏探索,像报告)

Will ChatGPT get you caught? Rethinking of Plagiarism Detection 推荐指数&#xff1a;2 主要内容 文章主要是研究chatgpt出现后&#xff0c;在学术界中可能出现的学术抄袭和剽窃现象。 这篇文章就比较了几种剽窃抄袭软件&#xff0c;来测试是否能够识别chatgpt编写的内…

软考中级-程序设计语言

&#xff08;1&#xff09;解释器解释源程序时不生成独立的目标代码&#xff0c;源程序和解释程序都参与到程序执行中。&#xff08;2&#xff09;编译器编译时生成独立的目标代码&#xff0c;运行时是运行与源程序等价的目标程序&#xff0c;源程序不参与执行。阶段补充&#…

如何选择最佳的 Node.js Docker 镜像

前言 选择一个Node.js的Docker镜像看起来像是一件小事&#xff0c;但是镜像的大小和潜在漏洞可能会对你的CI/CD流程和安全造成重大的影响。那我们如何选择一个最好Node.js Docker镜像呢&#xff1f;我们在使用FROM node:latest或只是FROM node时&#xff0c;很容易忽略他潜在的…

zlink 介绍

zlink 是一个基于 flink 开发的分布式数据开发工具&#xff0c;提供简单的易用的操作界面&#xff0c;降低用户学习 flink 的成本&#xff0c;缩短任务配置时间&#xff0c;避免配置过程中出现错误。用户可以通过拖拉拽的方式实现数据的实时同步&#xff0c;支持多数据源之间的…

Download android 12 source code and build img

sudo snap install curlThen follow https://mirrors.tuna.tsinghua.edu.cn/help/AOSP/install repo:mkdir ~/bin PATH~/bin:$PATH curl https://storage.googleapis.com/git-repo-downloads/repo > ~/bin/repo chmod ax ~/bin/repo由于首次同步需要下载约 60GB 数据&#x…

【动画图解】这个值取对了,ViewPager2才能纵享丝滑

前言 在前两篇文章中&#xff0c;我们通过一张张清晰明了的「示意图」&#xff0c;详细地复盘了RecyclerView「缓存复用机制」与「预拉取机制」的工作流程&#xff0c;这种「图解」创作形式也得到了来自不同平台读者们的一致认可。 而从本文开始&#xff0c;我们将正式进入Vi…

采用Spring配置文件管理Bean(1)

文章目录一、创建Maven项目二、创建杀龙任务类三、创建勇敢骑士类四、采用传统方式让勇敢骑士完成杀龙任务五、采用Spring容器让勇敢骑士完成杀龙任务1、创建日志属性文件2、创建Spring配置文件3、在Spring配置文件里创建Bean&#xff08;1&#xff09;创建杀龙任务Bean&#x…

VAmPI:一个包含了OWASP Top10漏洞的REST API安全学习平台

关于VAmPI VAmPI是一个包含了OWASP Top10漏洞的REST API安全学习平台&#xff0c;该平台基于Flask开发&#xff0c;该工具的主要目的是通过一个易受攻击的API来评估针对API安全检测工具的有效性&#xff0c;并帮助广大研究人员学习和了解API安全。 功能介绍 1、基于OWASP Top…

Spring入门学习

Spring入门学习 文章目录Spring入门学习Spring概述Spring FrameworkIOCIOC容器DIIOC容器的实现类①FileSystemXmlApplicationContext②ClassPathXmlApplicationContext基于XML管理bean入门案例创建类创建xml在Spring配置文件中配置bean测试Spring概述 Spring 是最受欢迎的企业级…

利用飞书来实现信息存储和在线远程预览

利用飞书来实现信息存储和在线远程预览 文章目录利用飞书来实现信息存储和在线远程预览1. 需求2. 实现方法2.1 创建表格2.2 创建飞书捷径2.3 客户端上传数据3. 实现远程预览1. 需求 在传统的远程监控和存储数据的项目中&#xff0c;我们需要一台有公网IP的服务器来存储设备发上…

75V的TVS二极管有哪些型号?常用的

瞬态抑制TVS二极管工作峰值反向电压最低3.3V&#xff0c;最高可达513V&#xff0c;甚至更高。很多电子工程师都知道&#xff0c;TVS二极管在实际应用选型过程中&#xff0c;第一步要确认的就是其工作峰值反向电压。2023年春节已过&#xff0c;东沃电子正月初八就开工了&#xf…

【JavaEE】多线程代码实例:单例模式与阻塞队列BlockingQueue

目录 单例模式&#xff1a; 什么是单例模式&#xff1f; 单例模式的实现方式&#xff1a; 饿汉模式&#xff1a; 懒汉模式&#xff1a; 基于并发编程对单例模式线程安全问题的讨论&#xff1a; 阻塞队列&#xff1a; 标准库中的阻塞队列&#xff1a; 自实现阻塞…