MyBatis多表關(guān)聯(lián)查詢的實(shí)現(xiàn)示例

一對多查詢
一對多關(guān)聯(lián)查詢是指在查詢一方對象的時候,同時將其所關(guān)聯(lián)的多方對象也都查詢出來。下面以班級 Classes 與學(xué)生 Student 間的一對多關(guān)系為例進(jìn)行演示。一個班級有多個學(xué)生,一個學(xué)生只屬于一個班級。數(shù)據(jù)庫 student 表里面有個字段 classno 是外鍵,對應(yīng)主鍵表 Class 的主鍵 cid。
項(xiàng)目案例:查詢班級號為 1801 的班級,同時遍歷該班級的所有的學(xué)生信息
實(shí)現(xiàn)步驟:
【1】在 MySQL 中創(chuàng)建數(shù)據(jù)庫 studentdb,創(chuàng)建表 student 和classes,并添加若干測試用的數(shù)據(jù)記錄,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','計算機(jī)軟件1班');
INSERT INTO classes (cid, cname) VALUES('201802','計算機(jī)軟件2班');
【2】創(chuàng)建實(shí)體類 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 "學(xué)生編號:"+getSid()+",學(xué)生姓名:"+getSname()+",學(xué)生性別:"+getSex()+",學(xué)生年齡:"+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()+",班級學(xué)生:";
}
}
【3】創(chuàng)建 ClassesMapper.java 接口,并添加 findClassesById 方法
package cn.kgc.my01.mapper;
import cn.kgc.my01.entity.Classes;
public interface ClassesMapper {
Classes findClassesById(String id);
}
【4】創(chuàng)建 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">
<!--方式一:多表連接查詢方式,只用到 1條SQL語句-->
<resultMap id="classResultMap" type="classes">
<id property="cid" column="cid"/>
<result property="cname" column="cname"/>
<!--關(guān)聯(lián)屬性的映射關(guān)系-->
<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/> 標(biāo)簽體現(xiàn)出兩個實(shí)體對象間的關(guān)聯(lián)關(guān)系。其兩個屬性的解釋如下:
- property:指定關(guān)聯(lián)屬性,即 Class 類中的集合屬性 students。
- ofType:集合屬性的泛型類型,即 Student。
方式二:多表單獨(dú)查詢方式
多表連接查詢方式是將多張表進(jìn)行連接,連為一張表后進(jìn)行查詢。其查詢的本質(zhì)是一張表。而多表單獨(dú)查詢方式是多張表各自查詢各自的相關(guān)內(nèi)容,需要多張表的聯(lián)合數(shù)據(jù),再將主表的查詢結(jié)果聯(lián)合其它表的查詢結(jié)果,封裝為一個對象。
多個查詢是可以跨越多個映射文件的,即是可以跨越多個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">
<!--方式二:多表單獨(dú)查詢方式,也就是分步查詢-->
<resultMap id="classResultMap2" type="classes">
<id property="cid" column="cid"/>
<result property="cname" column="cname"/>
<!--關(guān)聯(lián)屬性的映射關(guān)系-->
<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>
<!-- 以下注釋部分屬于方式二: 多表單獨(dú)查詢方式 -->
<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" />
<!-- 關(guān)聯(lián)屬性的映射關(guān)系 -->
<!-- 集合的數(shù)據(jù)來自指定的select查詢,該select查詢的動態(tài)參數(shù)來自column指定的字段值 -->
<collection property="students" ofType="Student" select="selectStudentsByClasses" column="cid"/>
</resultMap>
<!-- 多表單獨(dú)查詢,查多方的表 -->
<select id="selectStudentsByClasses" resultMap="studentResultMap">
select * from student where calssno=#{cid}
</select>
<!-- 多表單獨(dú)查詢,查一方的表 -->
<select id="findClassesById" parameterType="String" resultMap="classesResultMap">
select cid,cname from classes
where cid=#{cid}
</select>
</mapper>

【5】創(chuàng)建 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());
}
}
}
方式一:多表連接查詢方式測試結(jié)果:
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,班級名稱:計算機(jī)軟件1班,班級學(xué)生: 學(xué)生編號:1,學(xué)生姓名:張飛,學(xué)生性別:男,學(xué)生年齡:18 學(xué)生編號:2,學(xué)生姓名:李白,學(xué)生性別:男,學(xué)生年齡:20 學(xué)生編號:3,學(xué)生姓名:張無忌,學(xué)生性別:男,學(xué)生年齡:19 學(xué)生編號:4,學(xué)生姓名:趙敏,學(xué)生性別:女,學(xué)生年齡:17
可以發(fā)現(xiàn),只有一條 SQL 語句,并且是多表聯(lián)查。
方式二:多表單獨(dú)查詢方式測試結(jié)果:
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,班級名稱:計算機(jī)軟件1班,班級學(xué)生: 學(xué)生編號:1,學(xué)生姓名:張飛,學(xué)生性別:男,學(xué)生年齡:18 學(xué)生編號:2,學(xué)生姓名:李白,學(xué)生性別:男,學(xué)生年齡:20 學(xué)生編號:3,學(xué)生姓名:張無忌,學(xué)生性別:男,學(xué)生年齡:19 學(xué)生編號:4,學(xué)生姓名:趙敏,學(xué)生性別:女,學(xué)生年齡:17
可以發(fā)現(xiàn),其 SQL 語句是兩條,即各查各的,共用同一個參數(shù)。第 1 條先查一方的表,第 2 條再查多方的表。
多對一查詢
多對一關(guān)聯(lián)查詢是指在查詢多方對象的時候,同時將其所關(guān)聯(lián)的一方對象也查詢出來。
由于在查詢多方對象時也是一個一個查詢,所以多對一關(guān)聯(lián)查詢,其實(shí)就是一對一關(guān)聯(lián)查詢。即一對一關(guān)聯(lián)查詢的實(shí)現(xiàn)方式與多對一的實(shí)現(xiàn)方式是相同的。 配置多對一關(guān)聯(lián)的重點(diǎn)在于“多方”的映射文件要有 <association> 屬性關(guān)聯(lián)“一方”。
項(xiàng)目案例: 查詢學(xué)號為1的學(xué)生,同時獲取他所在班級的完整信息
實(shí)現(xiàn)步驟:
【1】創(chuàng)建 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】創(chuàng)建 StudentMapper.xml 映射文件,有以下兩種方式:
方式一:多表聯(lián)合查詢。
<!-- 多表聯(lián)合查詢 -->
<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" />
<!-- 關(guān)聯(lián)屬性 -->
<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>方式二:多表單獨(dú)查詢。
<!-- 以下注釋的是方式二:多表單獨(dú)查詢 -->
<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" />
<!-- 關(guān)聯(lián)屬性 -->
<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】創(chuàng)建 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());
}
}
方式一:多表聯(lián)合查詢方式測試結(jié)果:
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
學(xué)生編號:1,學(xué)生姓名:張飛,學(xué)生性別:男,學(xué)生年齡:18
所在班級:
Classes(cid=201801, cname=計算機(jī)軟件1班, students=null)
可以發(fā)現(xiàn),它發(fā)出的 SQL 語句是多表查詢。
方式一:多表單獨(dú)查詢方式測試結(jié)果:
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 學(xué)生編號:1,學(xué)生姓名:張飛,學(xué)生性別:男,學(xué)生年齡:18 所在班級: Classes(cid=201801, cname=計算機(jī)軟件1班, students=null)
可以發(fā)現(xiàn),它發(fā)出的 SQL 語句是兩條,即各查各的,共用同一個參數(shù)。
自連接查詢
自連接的查詢可以用一對多來處理,也可以用多對一來處理。例如,員工表,每個員工都有一個上司,但上司同時也是員工表的一條記錄,這種情況可用自連接查詢出每個員工對應(yīng)的上司信息,也可以查出每個上司有哪些下屬員工。
使用多對一的方式實(shí)現(xiàn)自連接
項(xiàng)目案例:查詢員工的信息及對應(yīng)的上司信息。
思路分析: 可將員工當(dāng)做多方,上司當(dāng)做一方。
實(shí)現(xiàn)步驟:
【1】修改數(shù)據(jù)庫。
添加一個表 employee 并插入測試數(shù)據(jù),具體如下:
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】創(chuàng)建實(shí)體類 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();
}
}
可以發(fā)現(xiàn),里面存在著嵌套,Employee 里面的一個屬性 leader 本身就是 Employee 類型。
【3】創(chuàng)建 EmployeeMapper.java 接口,添加 findEmployeeAndLeaderById 方法如下:
package cn.kgc.my01.mapper;
import cn.kgc.my01.entity.Employee;
public interface EmployeeMapper {
Employee findEmployeeAndLeaderById(int id);
}【4】創(chuàng)建 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】創(chuàng)建 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());
}
}
測試結(jié)果: 查詢員工
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 語句中發(fā)現(xiàn),出現(xiàn)了 3 條 SQL 語句,這個查詢存在嵌套,先查員工1,然后查他的直接上司3,再查上司的上司4。這種情況不影響什么,甚至可以實(shí)現(xiàn)直接輸出上司的上司,但要注意輸出語句不要出現(xiàn)地柜,即輸出語句不要出現(xiàn)輸出上司。
要同時查上司的上司,只需要在上面的測試類中多加一條語句:
System.out.println("他的上司的上司是:"+leader.getLeader().toString());
使用一對多的方式實(shí)現(xiàn)自連接
項(xiàng)目案例:查詢某位領(lǐng)導(dǎo)及其直接下屬員工。
思路分析: 可用一對多的方式來實(shí)現(xiàn),員工(領(lǐng)導(dǎo))當(dāng)作一方,員工(下屬)當(dāng)作多方。
實(shí)現(xiàn)步驟:
【1】修改實(shí)體類 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 的映射方法內(nèi)容如下:
<!-- 一對多的方式實(shí)現(xiàn)自連接 -->
<resultMap id="empResultMap2" type="employee">
<id property="empid" column="empid" />
<result property="empname" column="empname" />
<result property="job" column="job" />
<!-- 關(guān)聯(lián)屬性的映射關(guān)系
集合的數(shù)據(jù)來自指定的select查詢,該select查詢的動態(tài)參數(shù)來自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 測試類中,添加如下內(nèi)容:
@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());
}
}測試結(jié)果: 查詢經(jīng)理
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
多對多查詢
原理: 多對多可以分拆成兩個一對多來處理,需要一個中間表,各自與中間表實(shí)現(xiàn)一對多的關(guān)系。
項(xiàng)目案例:一個學(xué)生可以選人修多門課程,一門課程可以給多個學(xué)生選修,課程與學(xué)生之間是典型的多對多。實(shí)現(xiàn)查詢一個學(xué)生信息,同時查出他的所有選修課,還有實(shí)現(xiàn)查詢一門課程信息,同時查出所有的選修了該課程的學(xué)生信息。
思路分析: 多對多需要第三表來體現(xiàn),數(shù)據(jù)庫中除了課程表,學(xué)生表,還需要學(xué)生課程表。
實(shí)現(xiàn)步驟:
【1】修改數(shù)據(jù)庫,代碼如下:
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','計算機(jī)軟件1班');
INSERT INTO classes (cid, cname) VALUES('201802','計算機(jī)軟件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】新增實(shí)體類 Course 和修改實(shí)體類 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】配置對應(yīng)的 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" />
<!-- 關(guān)聯(lián)屬性的映射關(guān)系 -->
<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】創(chuàng)建測試類 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());
}
}
}
測試結(jié)果:

【6】新建 CourseMapper.java 接口,并添加一個方法如下:
package cn.kgc.my01.mapper;
import cn.kgc.my01.entity.Course;
public interface CourseMapper {
//根據(jù)id查找課程,即時獲取選個性該課程的學(xué)生
public Course searchCourseById(int id);
}【7】配置對應(yīng)的 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" />
<!-- 關(guān)聯(lián)屬性的映射關(guān)系 -->
<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】創(chuàng)建測試類 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("-------該課程有以下學(xué)生選修:------");
List<Student> students=course.getStudents();
for(Student student:students){
System.out.println(student.show());
}
}
}測試效果:

到此這篇關(guān)于MyBatis多表關(guān)聯(lián)查詢的實(shí)現(xiàn)示例的文章就介紹到這了,更多相關(guān)MyBatis多表關(guān)聯(lián)查詢內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- MyBatis-Plus實(shí)現(xiàn)多表聯(lián)查的方法實(shí)戰(zhàn)
- Mybatis-Plus多表關(guān)聯(lián)查詢的使用案例解析
- mybatis-plus多表聯(lián)查join的實(shí)現(xiàn)
- MyBatis-Plus多表聯(lián)查(動態(tài)查詢)的項(xiàng)目實(shí)踐
- MyBatis實(shí)現(xiàn)多表聯(lián)查的詳細(xì)代碼
- MyBatis-Plus多表聯(lián)查的實(shí)現(xiàn)方法(動態(tài)查詢和靜態(tài)查詢)
- Spring boot2基于Mybatis實(shí)現(xiàn)多表關(guān)聯(lián)查詢
- Mybatis-Plus 多表聯(lián)查分頁的實(shí)現(xiàn)代碼
- MyBatis-Flex實(shí)現(xiàn)多表聯(lián)查(自動映射)
相關(guān)文章
SpringBoot發(fā)送短信驗(yàn)證碼的實(shí)例
第三方短信發(fā)送平臺有很多種,各個平臺有各自的優(yōu)缺點(diǎn),在選擇的時候可以根據(jù)自己的具體實(shí)際情況定奪,本文主要介紹了SpringBoot發(fā)送短信驗(yàn)證碼的實(shí)例,感興趣的可以了解一下2022-02-02
關(guān)于spring依賴注入的方式以及優(yōu)缺點(diǎn)
這篇文章主要介紹了關(guān)于spring依賴注入的方式以及優(yōu)缺點(diǎn),依賴注入,是IOC的一個方面,是個通常的概念,它有多種解釋,這概念是說你不用創(chuàng)建對象,而只需要描述它如何被創(chuàng)建,需要的朋友可以參考下2023-07-07
Java異常中toString()和getMessage()區(qū)別
在java異常體系中,要打印異常信息,可以通過:e.getMessage() 、 e.toString() e.printStackTrace() 等方法打印,本文主要介紹了Java異常中toString()和getMessage()區(qū)別,具有一定的參考價值,感興趣的可以了解一下2024-01-01
Java中HashMap和Hashtable的區(qū)別淺析
這篇文章主要介紹了Java中HashMap和Hashtable的區(qū)別淺析,本文總結(jié)了6條它們之間的不同之處,需要的朋友可以參考下2015-03-03
Java利用MD5加鹽實(shí)現(xiàn)對密碼進(jìn)行加密處理
在開發(fā)的時候,有一些敏感信息是不能直接通過明白直接保存到數(shù)據(jù)庫的。最經(jīng)典的就是密碼了。如果直接把密碼以明文的形式入庫,不僅會泄露用戶的隱私,對系統(tǒng)也是極其的不厲。本文就來和大家介紹一下如何對密碼進(jìn)行加密處理,感興趣的可以了解一下2023-02-02
idea運(yùn)行tomcat報錯找不到catalina.bat,系統(tǒng)找不到指定的文件問題
這篇文章主要介紹了idea運(yùn)行tomcat報錯找不到catalina.bat,系統(tǒng)找不到指定的文件問題,具有很好的參考價值,希望對大家有所幫助,2023-11-11
java正則表達(dá)式之Pattern與Matcher類詳解
這篇文章主要給大家介紹了關(guān)于java正則表達(dá)式之Pattern與Matcher類的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09

