当前位置: 首页 > news >正文

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 mapperPUBLIC "-//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,studentwhere 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 mapperPUBLIC "-//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 classeswhere 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;@Beforepublic void init(){try {System.out.println("########");InputStream resourceAsStream = Resources.getResourceAsStream("config/mybatis-config.xml");factory = new SqlSessionFactoryBuilder().build(resourceAsStream);} catch (IOException e) {e.printStackTrace();}}@Testpublic 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,studentwhere classes.cid=student.classnoand 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;@Beforepublic void init(){try {InputStream resourceAsStream = Resources.getResourceAsStream("config/mybatis-config.xml");factory = new SqlSessionFactoryBuilder().build(resourceAsStream);} catch (IOException e) {e.printStackTrace();}}@Testpublic 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 mapperPUBLIC "-//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;@Beforepublic void init(){try {InputStream resourceAsStream = Resources.getResourceAsStream("config/mybatis-config.xml");factory = new SqlSessionFactoryBuilder().build(resourceAsStream);} catch (IOException e) {e.printStackTrace();}}@Testpublic 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 测试类中,添加如下内容:

    @Testpublic 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 mapperPUBLIC "-//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,studentcoursewhere course.courseid=studentcourse.courseidand 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;@Beforepublic void init(){try {InputStream resourceAsStream = Resources.getResourceAsStream("config/mybatis-config.xml");factory = new SqlSessionFactoryBuilder().build(resourceAsStream);} catch (IOException e) {e.printStackTrace();}}@Testpublic 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 mapperPUBLIC "-//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,studentcoursewhere course.courseid=studentcourse.courseidand 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;@Beforepublic void init(){try {InputStream resourceAsStream = Resources.getResourceAsStream("config/mybatis-config.xml");factory = new SqlSessionFactoryBuilder().build(resourceAsStream);} catch (IOException e) {e.printStackTrace();}}@Testpublic 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知识。与此同时,对于日常生活有困扰的朋友,欢迎阅读我的第四栏目:《国学周更—心性养成之路》,学习技术的同时,我们也注重了心性的养成。

在这里插入图片描述

相关文章:

MyBatis 多表关联查询

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…...

《NFL橄榄球》:克利夫兰布朗·橄榄1号位

克利夫兰布朗&#xff08;英语&#xff1a;Cleveland Browns&#xff09;是一支职业美式橄榄球球队&#xff0c;位于俄亥俄州克利夫兰。 布朗隶属于美国全国橄榄球联盟(NFL)的北区&#xff0c;主场位于第一能源体育场。球队在1946年与AAFC联盟一同成立&#xff0c;并在1946年到…...

InstructGPT笔记

一、InstructGPT是在GPT3上微调&#xff0c;ChatGPT是在GPT3.5上微调 二、该论文展示了怎么样对语言模型和人类意图之间进行匹配&#xff0c;方法是在人类的反馈上进行微调。 **三、方法简介&#xff1a;**收集很多问题&#xff0c;使用标注工具将问题的答案写出来&#xff0…...

【uniapp】getOpenerEventChannel().once 接收参数无效的解决方案

uniapp项目开发跨平台应用常会遇到接收参数无效的问题&#xff0c;无法判断是哪里出错了&#xff0c;这里是讲替代的方案&#xff0c;现有三种方案可选。 原因 一般我们是这样处理向另一个页面传参&#xff0c;代码是这样写的 //... let { title, type, rank } args; uni.n…...

ELK分布式日志收集快速入门-(二)kafka进阶-快速安装可视化管理界面-(单节点部署)

目录安装前准备安装中安装成功安装前准备 安装kafka-参考博客 (10条消息) ELK分布式日志收集快速入门-&#xff08;一&#xff09;-kafka单体篇_康世行的博客-CSDN博客 安装zk 参考博客 (10条消息) 快速搭建-分布式远程调用框架搭建-dubbozookperspringboot demo 演示_康世行的…...

线程的创建

1. 多线程常用函数 1.1 创建一条新线程pthread_create 对此函数使用注意以下几点&#xff1a; 线程例程指的是&#xff1a;如果线程创建成功&#xff0c;则该线程会立即执行的函数。POSIX线程库的所有API对返回值的处理原则一致&#xff1a;成功返回0&#xff0c;失败返回错误…...

分布式之Paxos共识算法分析

写在前面 分布式共识是分布式系统中的重要内容&#xff0c;本文来一起看下&#xff0c;一种历史悠久&#xff08;1998由兰伯特提出&#xff0c;并助其获得2003年图灵奖&#xff09;的实现分布式共识的算法Paxos。Paxos主要分为两部分&#xff0c;Basic Paxos和Multi-Paxos,其中…...

35岁测试工程师,面临中年危机,我该如何自救...

被辞的原因 最近因故来了上海&#xff0c;联系上了一位许久不见的老朋友&#xff0c;老王&#xff1b;老王和我是大学同学&#xff0c;毕业之后他去了上海&#xff0c;我来到广州。因为我们大学专业关系&#xff0c;从12年毕业以后我们从事着相同的职业&#xff0c;软件自动化…...

时间轮算法概念

概述 在一些中间件中我们经常见到时间轮控制并发和熔断。 那么这个时间轮具体是什么呢&#xff0c;又是怎么使用的呢。 简介 其实时间轮可以简单的理解成我们日常生活中的时钟。 时钟里的指针一直在不停的转动&#xff0c;利用这个我们可以实现定时任务&#xff0c;目前lin…...

[SCTF2019]babyre 题解

对未来的真正慷慨&#xff0c;是把一切献给现在。 ——加缪 目录 1.查壳 2.处理花指令&#xff0c;找到main函数 这一操作过程可以参考下面的视频&#xff1a; 3.静态分析第一部分,psword1 4.静态分析第二部分,psword2 5.静态分析第五部分&#xff0c;psword3 6.根据ps…...

全志H3系统移植 | 移植主线最新uboot 2023.04和kernel 6.1.11到Nanopi NEO开发板

文章目录 环境说明uboot移植kernel移植rootfs移植测试环境说明 OS:Ubuntu 20.04.5 LTSGCC:arm-none-linux-gnueabihf-gcc 10.3.0编译器下载地址:Downloads | GNU-A Downloads – Arm Developer uboot移植 当前最新版本v2023.04-rc2下载地址:https://github.com/u-boot/u-…...

vue项目第四天

使用elementui tabplane组件实现历史访问记录组件的二次封装<el-tabs type"border-card"><el-tab-pane label"用户管理">用户管理</el-tab-pane><el-tab-pane label"配置管理">配置管理</el-tab-pane><el-tab-…...

「C语言进阶」数据内存的存储

&#x1f680;&#x1f680;&#x1f680;大家觉不错的话&#xff0c;就恳求大家点点关注&#xff0c;点点小爱心&#xff0c;指点指点&#x1f680;&#x1f680;&#x1f680; 目录 &#x1f430;数据类型的介绍 &#x1f430;类型的意义 &#x1f430;数据类型的基本归类…...

面试必问:进程和线程的区别(从操作系统层次理解)

1.什么是进程&#xff1f;为什么要有进程&#xff1f; 进程有一个相当精简的解释&#xff1a;进程是对操作系统上正在运行程序的一个抽象。 这个概念确实挺抽象&#xff0c;仔细想想却也挺精准。 我们平常使用计算机&#xff0c;都会在同一时间做许多事&#xff0c;比如边看…...

ModuleNotFoundError: No module named ‘apex‘与 error: legacy-install-failure

ModuleNotFoundError: No module named ‘apex’ ModuleNotFoundError: No module named apex 表示 Python 在搜索模块时无法找到名为 apex 的模块。这通常是因为您没有安装 apex 模块或安装不正确。 apex 是一个针对混合精度训练和优化的 PyTorch 扩展库&#xff0c;您可以通过…...

Python3 VScode 配置

Python3 VScode 配置 在上一章节中我们已经安装了 Python 的环境&#xff0c;本章节我们将介绍 Python VScode 的配置。 准备工作&#xff1a; 安装 VS Code 安装 VS Code Python 扩展 安装 Python 3 安装 VS Code VSCode&#xff08;全称&#xff1a;Visual Studio Code&…...

VMware 修复了三个身份认证绕过漏洞

Bleeping Computer 网站披露&#xff0c;VMware 近期发布了安全更新&#xff0c;以解决 Workspace ONE Assist 解决方案中的三个严重漏洞&#xff0c;分别追踪为 CVE-2022-31685&#xff08;认证绕过&#xff09;、CVE-2022-31686 &#xff08;认证方法失败&#xff09;和 CVE-…...

实现一个简单的Database10(译文)

GreatSQL社区原创内容未经授权不得随意使用&#xff0c;转载请联系小编并注明来源。GreatSQL是MySQL的国产分支版本&#xff0c;使用上与MySQL一致。作者&#xff1a; 花家舍文章来源&#xff1a;GreatSQL社区原创 前文回顾 实现一个简单的Database系列 译注&#xff1a;csta…...

CTF-取证题目解析-提供环境

一、安装 官网下载&#xff1a;Volatility 2.6 Release 1、将windows下载的volatility上传到 kali/home 文件夹里面 3、将home/kali/vol刚刚上传的 移动到use/sbin目录里面 mv volatility usr/local/sbin/ 切换到里面 cd /usr/local/sbin/volatility 输入配置环境echo $PAT…...

计算机基础 | 网络篇 | TCP/IP 四层模型

前沿&#xff1a;撰写博客的目的是为了再刷时回顾和进一步完善&#xff0c;其次才是以教为学&#xff0c;所以如果有些博客写的较简陋&#xff0c;是为了保持进度不得已而为之&#xff0c;还请大家多多见谅。 一、OSI 七层模型 参考文章&#xff1a;OSI 和 TCP/IP 网络分层模型…...

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)

服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...

EtherNet/IP转DeviceNet协议网关详解

一&#xff0c;设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络&#xff0c;本网关连接到EtherNet/IP总线中做为从站使用&#xff0c;连接到DeviceNet总线中做为从站使用。 在自动…...

汇编常见指令

汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX&#xff08;不访问内存&#xff09;XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...

2025季度云服务器排行榜

在全球云服务器市场&#xff0c;各厂商的排名和地位并非一成不变&#xff0c;而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势&#xff0c;对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析&#xff1a; 一、全球“三巨头”…...

AirSim/Cosys-AirSim 游戏开发(四)外部固定位置监控相机

这个博客介绍了如何通过 settings.json 文件添加一个无人机外的 固定位置监控相机&#xff0c;因为在使用过程中发现 Airsim 对外部监控相机的描述模糊&#xff0c;而 Cosys-Airsim 在官方文档中没有提供外部监控相机设置&#xff0c;最后在源码示例中找到了&#xff0c;所以感…...

Qemu arm操作系统开发环境

使用qemu虚拟arm硬件比较合适。 步骤如下&#xff1a; 安装qemu apt install qemu-system安装aarch64-none-elf-gcc 需要手动下载&#xff0c;下载地址&#xff1a;https://developer.arm.com/-/media/Files/downloads/gnu/13.2.rel1/binrel/arm-gnu-toolchain-13.2.rel1-x…...

MySQL 索引底层结构揭秘:B-Tree 与 B+Tree 的区别与应用

文章目录 一、背景知识&#xff1a;什么是 B-Tree 和 BTree&#xff1f; B-Tree&#xff08;平衡多路查找树&#xff09; BTree&#xff08;B-Tree 的变种&#xff09; 二、结构对比&#xff1a;一张图看懂 三、为什么 MySQL InnoDB 选择 BTree&#xff1f; 1. 范围查询更快 2…...

Docker拉取MySQL后数据库连接失败的解决方案

在使用Docker部署MySQL时&#xff0c;拉取并启动容器后&#xff0c;有时可能会遇到数据库连接失败的问题。这种问题可能由多种原因导致&#xff0c;包括配置错误、网络设置问题、权限问题等。本文将分析可能的原因&#xff0c;并提供解决方案。 一、确认MySQL容器的运行状态 …...

LLaMA-Factory 微调 Qwen2-VL 进行人脸情感识别(二)

在上一篇文章中,我们详细介绍了如何使用LLaMA-Factory框架对Qwen2-VL大模型进行微调,以实现人脸情感识别的功能。本篇文章将聚焦于微调完成后,如何调用这个模型进行人脸情感识别的具体代码实现,包括详细的步骤和注释。 模型调用步骤 环境准备:确保安装了必要的Python库。…...

从物理机到云原生:全面解析计算虚拟化技术的演进与应用

前言&#xff1a;我的虚拟化技术探索之旅 我最早接触"虚拟机"的概念是从Java开始的——JVM&#xff08;Java Virtual Machine&#xff09;让"一次编写&#xff0c;到处运行"成为可能。这个软件层面的虚拟化让我着迷&#xff0c;但直到后来接触VMware和Doc…...