MyBatis 多表关联查询
✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。
🍎个人主页:Java Fans的博客
🍊个人信条:不迁怒,不贰过。小知识,大智慧。
💞当前专栏:SSM 框架从入门到精通
✨特色专栏:国学周更-心性养成之路
🥭本文内容:MyBatis 多表关联查询
文章目录
- 一对多查询
- 多对一查询
- 自连接查询
- 使用多对一的方式实现自连接
- 使用一对多的方式实现自连接
- 多对多查询
一对多查询
一对多关联查询是指在查询一方对象的时候,同时将其所关联的多方对象也都查询出来。下面以班级 Classes 与学生 Student 间的一对多关系为例进行演示。一个班级有多个学生,一个学生只属于一个班级。数据库 student 表里面有个字段 classno 是外键,对应主键表 Class 的主键 cid。
项目案例:查询班级号为 1801 的班级,同时遍历该班级的所有的学生信息
实现步骤:
【1】在 MySQL 中创建数据库 studentdb,创建表 student 和classes,并添加若干测试用的数据记录,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】创建实体类 Classes 和 Student 类
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"><!--方式一:多表连接查询方式,只用到 1条SQL语句--><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 多表关联查询
✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏…...
《NFL橄榄球》:克利夫兰布朗·橄榄1号位
克利夫兰布朗(英语:Cleveland Browns)是一支职业美式橄榄球球队,位于俄亥俄州克利夫兰。 布朗隶属于美国全国橄榄球联盟(NFL)的北区,主场位于第一能源体育场。球队在1946年与AAFC联盟一同成立,并在1946年到…...

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

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

ELK分布式日志收集快速入门-(二)kafka进阶-快速安装可视化管理界面-(单节点部署)
目录安装前准备安装中安装成功安装前准备 安装kafka-参考博客 (10条消息) ELK分布式日志收集快速入门-(一)-kafka单体篇_康世行的博客-CSDN博客 安装zk 参考博客 (10条消息) 快速搭建-分布式远程调用框架搭建-dubbozookperspringboot demo 演示_康世行的…...

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

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

35岁测试工程师,面临中年危机,我该如何自救...
被辞的原因 最近因故来了上海,联系上了一位许久不见的老朋友,老王;老王和我是大学同学,毕业之后他去了上海,我来到广州。因为我们大学专业关系,从12年毕业以后我们从事着相同的职业,软件自动化…...

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

[SCTF2019]babyre 题解
对未来的真正慷慨,是把一切献给现在。 ——加缪 目录 1.查壳 2.处理花指令,找到main函数 这一操作过程可以参考下面的视频: 3.静态分析第一部分,psword1 4.静态分析第二部分,psword2 5.静态分析第五部分,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语言进阶」数据内存的存储
🚀🚀🚀大家觉不错的话,就恳求大家点点关注,点点小爱心,指点指点🚀🚀🚀 目录 🐰数据类型的介绍 🐰类型的意义 🐰数据类型的基本归类…...

面试必问:进程和线程的区别(从操作系统层次理解)
1.什么是进程?为什么要有进程? 进程有一个相当精简的解释:进程是对操作系统上正在运行程序的一个抽象。 这个概念确实挺抽象,仔细想想却也挺精准。 我们平常使用计算机,都会在同一时间做许多事,比如边看…...
ModuleNotFoundError: No module named ‘apex‘与 error: legacy-install-failure
ModuleNotFoundError: No module named ‘apex’ ModuleNotFoundError: No module named apex 表示 Python 在搜索模块时无法找到名为 apex 的模块。这通常是因为您没有安装 apex 模块或安装不正确。 apex 是一个针对混合精度训练和优化的 PyTorch 扩展库,您可以通过…...

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

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

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

CTF-取证题目解析-提供环境
一、安装 官网下载: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 四层模型
前沿:撰写博客的目的是为了再刷时回顾和进一步完善,其次才是以教为学,所以如果有些博客写的较简陋,是为了保持进度不得已而为之,还请大家多多见谅。 一、OSI 七层模型 参考文章:OSI 和 TCP/IP 网络分层模型…...

7.4.分块查找
一.分块查找的算法思想: 1.实例: 以上述图片的顺序表为例, 该顺序表的数据元素从整体来看是乱序的,但如果把这些数据元素分成一块一块的小区间, 第一个区间[0,1]索引上的数据元素都是小于等于10的, 第二…...

深入剖析AI大模型:大模型时代的 Prompt 工程全解析
今天聊的内容,我认为是AI开发里面非常重要的内容。它在AI开发里无处不在,当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗",或者让翻译模型 "将这段合同翻译成商务日语" 时,输入的这句话就是 Prompt。…...

【2025年】解决Burpsuite抓不到https包的问题
环境:windows11 burpsuite:2025.5 在抓取https网站时,burpsuite抓取不到https数据包,只显示: 解决该问题只需如下三个步骤: 1、浏览器中访问 http://burp 2、下载 CA certificate 证书 3、在设置--隐私与安全--…...
LLM基础1_语言模型如何处理文本
基于GitHub项目:https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken:OpenAI开发的专业"分词器" torch:Facebook开发的强力计算引擎,相当于超级计算器 理解词嵌入:给词语画"…...

12.找到字符串中所有字母异位词
🧠 题目解析 题目描述: 给定两个字符串 s 和 p,找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义: 若两个字符串包含的字符种类和出现次数完全相同,顺序无所谓,则互为…...
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南 在数字化营销时代,邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天,我们将深入解析邮件打开率、网站可用性、页面参与时…...
k8s从入门到放弃之HPA控制器
k8s从入门到放弃之HPA控制器 Kubernetes中的Horizontal Pod Autoscaler (HPA)控制器是一种用于自动扩展部署、副本集或复制控制器中Pod数量的机制。它可以根据观察到的CPU利用率(或其他自定义指标)来调整这些对象的规模,从而帮助应用程序在负…...

高效的后台管理系统——可进行二次开发
随着互联网技术的迅猛发展,企业的数字化管理变得愈加重要。后台管理系统作为数据存储与业务管理的核心,成为了现代企业不可或缺的一部分。今天我们要介绍的是一款名为 若依后台管理框架 的系统,它不仅支持跨平台应用,还能提供丰富…...

数据挖掘是什么?数据挖掘技术有哪些?
目录 一、数据挖掘是什么 二、常见的数据挖掘技术 1. 关联规则挖掘 2. 分类算法 3. 聚类分析 4. 回归分析 三、数据挖掘的应用领域 1. 商业领域 2. 医疗领域 3. 金融领域 4. 其他领域 四、数据挖掘面临的挑战和未来趋势 1. 面临的挑战 2. 未来趋势 五、总结 数据…...

dvwa11——XSS(Reflected)
LOW 分析源码:无过滤 和上一关一样,这一关在输入框内输入,成功回显 <script>alert(relee);</script> MEDIUM 分析源码,是把<script>替换成了空格,但没有禁用大写 改大写即可,注意函数…...