目录
- 一对多查询
- 多对一查询
- 自连接查询
- 多对多查询
一对多查询
一对多关联查询是指在查询一方对象的时候,同时将其所关联的多方对象也都查询出来。下面以班级 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() NOT NULL, | |
studentname VARCHAR() DEFAULT NULL, | |
gender CHAR() DEFAULT NULL, | |
age INT() DEFAULT NULL, | |
classno VARCHAR(), | |
PRIMARY KEY ( id ) | |
) | |
INSERT INTO student ( id , studentname , gender , age , classno ) VALUES (,'张飞','男',18,'201801'),(2,'李白','男',20,'201801'),(3,'张无忌','男',19,'201801'),(4,'赵敏','女',17,'201801'); | |
CREATE TABLE classes ( | |
cid VARCHAR (), | |
cname VARCHAR () | |
); | |
INSERT INTO classes (cid, cname) VALUES('','计算机软件1班'); | |
INSERT INTO classes (cid, cname) VALUES('','计算机软件2班'); |
【2】创建实体类 Classes 和 Student 类
Student 类如下:
package cn.kgc.my.entity; | |
import lombok.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.my.entity; | |
import lombok.Data; | |
import java.util.List; | |
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.my.mapper; | |
import cn.kgc.my.entity.Classes; | |
public interface ClassesMapper { | |
Classes findClassesById(String id); | |
} | |
【4】创建 ClassesMapper.xml 映射文件,有以下两种方式:
方式一:多表连接查询方式
这种方式只用到1条 SQL 语句,代码如下所示:
PUBLIC "-//mybatis.org//DTD Mapper.0//EN" | |
"https://mybatis.org/dtd/mybatis--mapper.dtd"> | |
<mapper namespace="cn.kgc.my.mapper.ClassesMapper"> | |
<!--方式一:多表连接查询方式,只用到条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,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 语句,代码如下所示:
PUBLIC "-//mybatis.org//DTD Mapper.0//EN" | |
"https://mybatis.org/dtd/mybatis--mapper.dtd"> | |
<mapper namespace="cn.kgc.my.mapper.ClassesMapper"> | |
<!--方式二:多表单独查询方式,也就是分步查询--> | |
<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> | |
<!-- 以下注释部分属于方式二: 多表单独查询方式 --> | |
<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.my.mapper; | |
import cn.kgc.my.entity.Classes; | |
import cn.kgc.my.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; | |
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(); | |
} | |
} | |
public void testFindClassesById() { | |
SqlSession sqlSession = factory.openSession(true); | |
ClassesMapper mapper = sqlSession.getMapper(ClassesMapper.class); | |
Classes classesById = mapper.findClassesById(""); | |
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:(String) | |
DEBUG [main] - <== Total: | |
班级编号:,班级名称:计算机软件1班,班级学生: | |
学生编号:,学生姓名:张飞,学生性别:男,学生年龄:18 | |
学生编号:,学生姓名:李白,学生性别:男,学生年龄:20 | |
学生编号:,学生姓名:张无忌,学生性别:男,学生年龄:19 | |
学生编号:,学生姓名:赵敏,学生性别:女,学生年龄:17 |
可以发现,只有一条 SQL 语句,并且是多表联查。
方式二:多表单独查询方式测试结果:
-02-15 10:56:49,965 [main] DEBUG DEBUG [main] - ==> Preparing: select cid,cname from classes where cid=? | |
DEBUG [main] - ==> Parameters:(String) | |
DEBUG [main] - ====> Preparing: select * from student where classno=? | |
DEBUG [main] - ====> Parameters:(String) | |
DEBUG [main] - <==== Total: | |
DEBUG [main] - <== Total: | |
班级编号:,班级名称:计算机软件1班,班级学生: | |
学生编号:,学生姓名:张飞,学生性别:男,学生年龄:18 | |
学生编号:,学生姓名:李白,学生性别:男,学生年龄:20 | |
学生编号:,学生姓名:张无忌,学生性别:男,学生年龄:19 | |
学生编号:,学生姓名:赵敏,学生性别:女,学生年龄:17 |
可以发现,其 SQL 语句是两条,即各查各的,共用同一个参数。第 1 条先查一方的表,第 2 条再查多方的表。
多对一查询
多对一关联查询是指在查询多方对象的时候,同时将其所关联的一方对象也查询出来。
由于在查询多方对象时也是一个一个查询,所以多对一关联查询,其实就是一对一关联查询。即一对一关联查询的实现方式与多对一的实现方式是相同的。 配置多对一关联的重点在于“多方”的映射文件要有 <association> 属性关联“一方”。
项目案例: 查询学号为1的学生,同时获取他所在班级的完整信息
实现步骤:
【1】创建 StudentMapper.java 接口,并添加方法 searchStudentsById(int id) 如下:
package cn.kgc.my.mapper; | |
import cn.kgc.my.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="studentResultMap" 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="studentResultMap"> | |
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.my.mapper; | |
import cn.kgc.my.entity.Classes; | |
import cn.kgc.my.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; | |
public void init(){ | |
try { | |
InputStream resourceAsStream = Resources.getResourceAsStream("config/mybatis-config.xml"); | |
factory = new SqlSessionFactoryBuilder().build(resourceAsStream); | |
} catch (IOException e) { | |
e.printStackTrace(); | |
} | |
} | |
public void testSearchStudentsById() { | |
SqlSession sqlSession = factory.openSession(true); | |
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); | |
Student student = mapper.searchStudentsById(); | |
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:(Integer) | |
DEBUG [main] - <== Total: | |
学生编号:,学生姓名:张飞,学生性别:男,学生年龄:18 | |
所在班级: | |
Classes(cid=, cname=计算机软件1班, students=null) |
可以发现,它发出的 SQL 语句是多表查询。
方式一:多表单独查询方式测试结果:
DEBUG [main] - ==> Preparing: select id,studentname,gender,age,classno from student where id=? | |
DEBUG [main] - ==> Parameters:(Integer) | |
DEBUG [main] - ====> Preparing: select cid,cname from classes where cid=? | |
DEBUG [main] - ====> Parameters:(String) | |
DEBUG [main] - <==== Total: | |
DEBUG [main] - <== Total: | |
学生编号:,学生姓名:张飞,学生性别:男,学生年龄:18 | |
所在班级: | |
Classes(cid=, cname=计算机软件1班, students=null) |
可以发现,它发出的 SQL 语句是两条,即各查各的,共用同一个参数。
自连接查询
自连接的查询可以用一对多来处理,也可以用多对一来处理。例如,员工表,每个员工都有一个上司,但上司同时也是员工表的一条记录,这种情况可用自连接查询出每个员工对应的上司信息,也可以查出每个上司有哪些下属员工。
使用多对一的方式实现自连接
项目案例:查询员工的信息及对应的上司信息。
思路分析: 可将员工当做多方,上司当做一方。
实现步骤:
【1】修改数据库。
添加一个表 employee 并插入测试数据,具体如下:
create table employee ( | |
empid double , | |
empname varchar (), | |
job varchar (), | |
leader double | |
); | |
insert into employee (empid, empname, job, leader) values('','jack','clerk','3'); | |
insert into employee (empid, empname, job, leader) values('','mike','salesman','3'); | |
insert into employee (empid, empname, job, leader) values('','john','manager','4'); | |
insert into employee (empid, empname, job, leader) values('','smith','president',NULL); | |
insert into employee (empid, empname, job, leader) values('','rose','salesman','3'); |
【2】创建实体类 Employee,代码如下:
package cn.kgc.my.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.my.mapper; | |
import cn.kgc.my.entity.Employee; | |
public interface EmployeeMapper { | |
Employee findEmployeeAndLeaderById(int id); | |
} |
【4】创建 EmployeeMapper.xml 映射文件,代码如下:
PUBLIC "-//mybatis.org//DTD Mapper.0//EN" | |
"https://mybatis.org/dtd/mybatis--mapper.dtd"> | |
<mapper namespace="cn.kgc.my.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.my.mapper; | |
import cn.kgc.my.entity.Classes; | |
import cn.kgc.my.entity.Employee; | |
import cn.kgc.my.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; | |
public void init(){ | |
try { | |
InputStream resourceAsStream = Resources.getResourceAsStream("config/mybatis-config.xml"); | |
factory = new SqlSessionFactoryBuilder().build(resourceAsStream); | |
} catch (IOException e) { | |
e.printStackTrace(); | |
} | |
} | |
public void testFindEmployeeAndLeaderById() { | |
SqlSession sqlSession = factory.openSession(true); | |
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class); | |
Employee employee=mapper.findEmployeeAndLeaderById(); | |
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:(Integer) | |
DEBUG [main] - ====> Preparing: select * from employee where empid=? | |
DEBUG [main] - ====> Parameters:(Integer) | |
DEBUG [main] - ======> Preparing: select * from employee where empid=? | |
DEBUG [main] - ======> Parameters:(Integer) | |
DEBUG [main] - <====== Total: | |
DEBUG [main] - <==== Total: | |
DEBUG [main] - <== Total: | |
员工编号:,员工姓名:jack,员工职位:clerk | |
他的上司是:员工编号:,员工姓名:john,员工职位:manager |
从上面的 SQL 语句中发现,出现了 3 条 SQL 语句,这个查询存在嵌套,先查员工1,然后查他的直接上司3,再查上司的上司4。这种情况不影响什么,甚至可以实现直接输出上司的上司,但要注意输出语句不要出现地柜,即输出语句不要出现输出上司。
要同时查上司的上司,只需要在上面的测试类中多加一条语句:
System.out.println("他的上司的上司是:"+leader.getLeader().toString());
使用一对多的方式实现自连接
项目案例:查询某位领导及其直接下属员工。
思路分析: 可用一对多的方式来实现,员工(领导)当作一方,员工(下属)当作多方。
实现步骤:
【1】修改实体类 Employee,代码如下:
package cn.kgc.my.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="empResultMap" 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="empResultMap"> | |
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(); | |
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:(Integer) | |
DEBUG [main] - ====> Preparing: select * from employee where leader=? | |
DEBUG [main] - ====> Parameters:.0(Double) | |
DEBUG [main] - <==== Total: | |
DEBUG [main] - <== Total: | |
员工编号:,员工姓名:smith,员工职位:president | |
他的直接下属有: | |
员工编号:,员工姓名:john,员工职位:manager |
多对多查询
原理: 多对多可以分拆成两个一对多来处理,需要一个中间表,各自与中间表实现一对多的关系。
项目案例:一个学生可以选人修多门课程,一门课程可以给多个学生选修,课程与学生之间是典型的多对多。实现查询一个学生信息,同时查出他的所有选修课,还有实现查询一门课程信息,同时查出所有的选修了该课程的学生信息。
思路分析: 多对多需要第三表来体现,数据库中除了课程表,学生表,还需要学生课程表。
实现步骤:
【1】修改数据库,代码如下:
CREATE DATABASE studentdb; | |
USE studentdb ; | |
DROP TABLE IF EXISTS student ; | |
CREATE TABLE student ( | |
id INT() NOT NULL, | |
studentname VARCHAR() DEFAULT NULL, | |
gender CHAR() DEFAULT NULL, | |
age INT() DEFAULT NULL, | |
classno VARCHAR(), | |
PRIMARY KEY ( id ) | |
) | |
INSERT INTO student ( id , studentname , gender , age , classno ) VALUES (,'张飞','男',18,'201801'),(2,'李白','男',20,'201801'),(3,'张无忌','男',19,'201801'),(4,'赵敏','女',17,'201801'); | |
CREATE TABLE classes ( | |
cid VARCHAR (), | |
cname VARCHAR () | |
); | |
INSERT INTO classes (cid, cname) VALUES('','计算机软件1班'); | |
INSERT INTO classes (cid, cname) VALUES('','计算机软件2班'); | |
CREATE TABLE employee ( | |
empid DOUBLE , | |
empname VARCHAR (), | |
job VARCHAR (), | |
leader DOUBLE | |
); | |
INSERT INTO employee (empid, empname, job, leader) VALUES('','jack','clerk','3'); | |
INSERT INTO employee (empid, empname, job, leader) VALUES('','mike','salesman','3'); | |
INSERT INTO employee (empid, empname, job, leader) VALUES('','john','manager','4'); | |
INSERT INTO employee (empid, empname, job, leader) VALUES('','smith','president',NULL); | |
INSERT INTO employee (empid, empname, job, leader) VALUES('','rose','salesman','3'); | |
CREATE TABLE course ( | |
courseid DOUBLE , | |
coursename VARCHAR () | |
); | |
INSERT INTO course (courseid, coursename) VALUES('','java'); | |
INSERT INTO course (courseid, coursename) VALUES('','android'); | |
INSERT INTO course (courseid, coursename) VALUES('','PHP'); | |
CREATE TABLE studentcourse ( | |
id DOUBLE , | |
studentid DOUBLE , | |
courseid DOUBLE | |
); | |
INSERT INTO studentcourse (id, studentid, courseid) VALUES('','1','1'); | |
INSERT INTO studentcourse (id, studentid, courseid) VALUES('','1','2'); | |
INSERT INTO studentcourse (id, studentid, courseid) VALUES('','2','1'); | |
INSERT INTO studentcourse (id, studentid, courseid) VALUES('','2','2'); | |
INSERT INTO studentcourse (id, studentid, courseid) VALUES('','3','1'); | |
INSERT INTO studentcourse (id, studentid, courseid) VALUES('','3','2'); | |
INSERT INTO studentcourse (id, studentid, courseid) VALUES('','1','3'); |
【2】新增实体类 Course 和修改实体类 Student 。
Course 类如下:
package cn.kgc.my.entity; | |
import lombok.Data; | |
import java.util.List; | |
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.my.mapper; | |
import cn.kgc.my.entity.Student; | |
public interface StudentMapper { | |
public Student searchStudentById(int id); | |
} |
【4】配置对应的 StudentMapper.xml 映射,代码如下:
PUBLIC "-//mybatis.org//DTD Mapper.0//EN" | |
"https://mybatis.org/dtd/mybatis--mapper.dtd"> | |
<mapper namespace="cn.kgc.my.mapper.StudentMapper"> | |
<resultMap id="studentMap" 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="studentMap"> | |
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.my.mapper; | |
import cn.kgc.my.entity.Classes; | |
import cn.kgc.my.entity.Course; | |
import cn.kgc.my.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; | |
public void init(){ | |
try { | |
InputStream resourceAsStream = Resources.getResourceAsStream("config/mybatis-config.xml"); | |
factory = new SqlSessionFactoryBuilder().build(resourceAsStream); | |
} catch (IOException e) { | |
e.printStackTrace(); | |
} | |
} | |
public void testSearchStudentsById() { | |
SqlSession sqlSession = factory.openSession(true); | |
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); | |
Student student = mapper.searchStudentById(); | |
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.my.mapper; | |
import cn.kgc.my.entity.Course; | |
public interface CourseMapper { | |
//根据id查找课程,即时获取选个性该课程的学生 | |
public Course searchCourseById(int id); | |
} |
【7】配置对应的 CourseMapper.xml 映射,代码如下:
PUBLIC "-//mybatis.org//DTD Mapper.0//EN" | |
"https://mybatis.org/dtd/mybatis--mapper.dtd"> | |
<mapper namespace="cn.kgc.my.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.my.mapper; | |
import cn.kgc.my.entity.Course; | |
import cn.kgc.my.entity.Employee; | |
import cn.kgc.my.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; | |
public void init(){ | |
try { | |
InputStream resourceAsStream = Resources.getResourceAsStream("config/mybatis-config.xml"); | |
factory = new SqlSessionFactoryBuilder().build(resourceAsStream); | |
} catch (IOException e) { | |
e.printStackTrace(); | |
} | |
} | |
public void testSearchCourseById() { | |
SqlSession sqlSession = factory.openSession(true); | |
CourseMapper mapper = sqlSession.getMapper(CourseMapper.class); | |
Course course = mapper.searchCourseById(); | |
System.out.println(course.toString()); | |
System.out.println("-------该课程有以下学生选修:------"); | |
List<Student> students=course.getStudents(); | |
for(Student student:students){ | |
System.out.println(student.show()); | |
} | |
} | |
} |
测试效果: