MyBatis多表關(guān)聯(lián)查詢的實(shí)現(xiàn)示例
一對(duì)多查詢
一對(duì)多關(guān)聯(lián)查詢是指在查詢一方對(duì)象的時(shí)候,同時(shí)將其所關(guān)聯(lián)的多方對(duì)象也都查詢出來。下面以班級(jí) Classes 與學(xué)生 Student 間的一對(duì)多關(guān)系為例進(jìn)行演示。一個(gè)班級(jí)有多個(gè)學(xué)生,一個(gè)學(xué)生只屬于一個(gè)班級(jí)。數(shù)據(jù)庫 student 表里面有個(gè)字段 classno 是外鍵,對(duì)應(yīng)主鍵表 Class 的主鍵 cid。
項(xiàng)目案例:查詢班級(jí)號(hào)為 1801 的班級(jí),同時(shí)遍歷該班級(jí)的所有的學(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ì)算機(jī)軟件1班'); INSERT INTO classes (cid, cname) VALUES('201802','計(jì)算機(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; //添加額外屬性:所在班級(jí) private Classes classes; public String show(){ return "學(xué)生編號(hào):"+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 "班級(jí)編號(hào):"+getCid()+",班級(jí)名稱:"+getCname()+",班級(jí)學(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/> 中,如果字段名與屬性名相同時(shí),可以在 <resultMap/> 中添加 autoMapping=“true” 來開啟自動(dòng)映射。
另外,在 “一方” 的映射文件中使用 <collection/> 標(biāo)簽體現(xiàn)出兩個(gè)實(shí)體對(duì)象間的關(guān)聯(lián)關(guān)系。其兩個(gè)屬性的解釋如下:
- 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é)果,封裝為一個(gè)對(duì)象。
多個(gè)查詢是可以跨越多個(gè)映射文件的,即是可以跨越多個(gè)namespace 的。在使用其它 namespace 的查詢時(shí),添加上其所在的 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查詢的動(dòng)態(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 班級(jí)編號(hào):201801,班級(jí)名稱:計(jì)算機(jī)軟件1班,班級(jí)學(xué)生: 學(xué)生編號(hào):1,學(xué)生姓名:張飛,學(xué)生性別:男,學(xué)生年齡:18 學(xué)生編號(hào):2,學(xué)生姓名:李白,學(xué)生性別:男,學(xué)生年齡:20 學(xué)生編號(hào):3,學(xué)生姓名:張無忌,學(xué)生性別:男,學(xué)生年齡:19 學(xué)生編號(hào):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 班級(jí)編號(hào):201801,班級(jí)名稱:計(jì)算機(jī)軟件1班,班級(jí)學(xué)生: 學(xué)生編號(hào):1,學(xué)生姓名:張飛,學(xué)生性別:男,學(xué)生年齡:18 學(xué)生編號(hào):2,學(xué)生姓名:李白,學(xué)生性別:男,學(xué)生年齡:20 學(xué)生編號(hào):3,學(xué)生姓名:張無忌,學(xué)生性別:男,學(xué)生年齡:19 學(xué)生編號(hào):4,學(xué)生姓名:趙敏,學(xué)生性別:女,學(xué)生年齡:17
可以發(fā)現(xiàn),其 SQL 語句是兩條,即各查各的,共用同一個(gè)參數(shù)。第 1 條先查一方的表,第 2 條再查多方的表。
多對(duì)一查詢
多對(duì)一關(guān)聯(lián)查詢是指在查詢多方對(duì)象的時(shí)候,同時(shí)將其所關(guān)聯(lián)的一方對(duì)象也查詢出來。
由于在查詢多方對(duì)象時(shí)也是一個(gè)一個(gè)查詢,所以多對(duì)一關(guān)聯(lián)查詢,其實(shí)就是一對(duì)一關(guān)聯(lián)查詢。即一對(duì)一關(guān)聯(lián)查詢的實(shí)現(xiàn)方式與多對(duì)一的實(shí)現(xiàn)方式是相同的。 配置多對(duì)一關(guān)聯(lián)的重點(diǎn)在于“多方”的映射文件要有 <association> 屬性關(guān)聯(lián)“一方”。
項(xiàng)目案例: 查詢學(xué)號(hào)為1的學(xué)生,同時(shí)獲取他所在班級(jí)的完整信息
實(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("所在班級(jí):"); 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é)生編號(hào):1,學(xué)生姓名:張飛,學(xué)生性別:男,學(xué)生年齡:18 所在班級(jí): Classes(cid=201801, cname=計(jì)算機(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é)生編號(hào):1,學(xué)生姓名:張飛,學(xué)生性別:男,學(xué)生年齡:18 所在班級(jí): Classes(cid=201801, cname=計(jì)算機(jī)軟件1班, students=null)
可以發(fā)現(xiàn),它發(fā)出的 SQL 語句是兩條,即各查各的,共用同一個(gè)參數(shù)。
自連接查詢
自連接的查詢可以用一對(duì)多來處理,也可以用多對(duì)一來處理。例如,員工表,每個(gè)員工都有一個(gè)上司,但上司同時(shí)也是員工表的一條記錄,這種情況可用自連接查詢出每個(gè)員工對(duì)應(yīng)的上司信息,也可以查出每個(gè)上司有哪些下屬員工。
使用多對(duì)一的方式實(shí)現(xiàn)自連接
項(xiàng)目案例:查詢員工的信息及對(duì)應(yīng)的上司信息。
思路分析: 可將員工當(dāng)做多方,上司當(dāng)做一方。
實(shí)現(xiàn)步驟:
【1】修改數(shù)據(jù)庫。
添加一個(gè)表 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 "員工編號(hào):"+getEmpid()+",員工姓名:"+getEmpname()+",員工職位:"+getJob(); } }
可以發(fā)現(xiàn),里面存在著嵌套,Employee 里面的一個(gè)屬性 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 員工編號(hào):1,員工姓名:jack,員工職位:clerk 他的上司是:員工編號(hào):3,員工姓名:john,員工職位:manager
從上面的 SQL 語句中發(fā)現(xiàn),出現(xiàn)了 3 條 SQL 語句,這個(gè)查詢存在嵌套,先查員工1,然后查他的直接上司3,再查上司的上司4。這種情況不影響什么,甚至可以實(shí)現(xiàn)直接輸出上司的上司,但要注意輸出語句不要出現(xiàn)地柜,即輸出語句不要出現(xiàn)輸出上司。
要同時(shí)查上司的上司,只需要在上面的測試類中多加一條語句:
System.out.println("他的上司的上司是:"+leader.getLeader().toString());
使用一對(duì)多的方式實(shí)現(xiàn)自連接
項(xiàng)目案例:查詢某位領(lǐng)導(dǎo)及其直接下屬員工。
思路分析: 可用一對(duì)多的方式來實(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 "員工編號(hào):"+getEmpid()+",員工姓名:"+getEmpname()+",員工職位:"+getJob(); } }
【2】在 EmployeeMapper.java 接口中,添加 findLeaderAndEmployeesById 方法如下:
Employee findLeaderAndEmployeesById(int id);
【3】在 EmployeeMapper.xml 映射文件中,添加 findEmployeeAndLeaderById 的映射方法內(nèi)容如下:
<!-- 一對(duì)多的方式實(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查詢的動(dòng)態(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 員工編號(hào):4,員工姓名:smith,員工職位:president 他的直接下屬有: 員工編號(hào):3,員工姓名:john,員工職位:manager
多對(duì)多查詢
原理: 多對(duì)多可以分拆成兩個(gè)一對(duì)多來處理,需要一個(gè)中間表,各自與中間表實(shí)現(xiàn)一對(duì)多的關(guān)系。
項(xiàng)目案例:一個(gè)學(xué)生可以選人修多門課程,一門課程可以給多個(gè)學(xué)生選修,課程與學(xué)生之間是典型的多對(duì)多。實(shí)現(xiàn)查詢一個(gè)學(xué)生信息,同時(shí)查出他的所有選修課,還有實(shí)現(xiàn)查詢一門課程信息,同時(shí)查出所有的選修了該課程的學(xué)生信息。
思路分析: 多對(duì)多需要第三表來體現(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ì)算機(jī)軟件1班'); INSERT INTO classes (cid, cname) VALUES('201802','計(jì)算機(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 "課程編號(hào):"+getCourseid()+",課程名稱:"+getCoursename(); } }
Student類如下,添加一個(gè)屬性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 接口,并添加一個(gè)方法如下:
package cn.kgc.my01.mapper; import cn.kgc.my01.entity.Student; public interface StudentMapper { public Student searchStudentById(int id); }
【4】配置對(duì)應(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 接口,并添加一個(gè)方法如下:
package cn.kgc.my01.mapper; import cn.kgc.my01.entity.Course; public interface CourseMapper { //根據(jù)id查找課程,即時(shí)獲取選個(gè)性該課程的學(xué)生 public Course searchCourseById(int id); }
【7】配置對(duì)應(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)容請(qǐng)搜索腳本之家以前的文章或繼續(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)查(動(dòng)態(tài)查詢)的項(xiàng)目實(shí)踐
- MyBatis實(shí)現(xiàn)多表聯(lián)查的詳細(xì)代碼
- MyBatis-Plus多表聯(lián)查的實(shí)現(xiàn)方法(動(dòng)態(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)查(自動(dòng)映射)
相關(guān)文章
SpringBoot發(fā)送短信驗(yàn)證碼的實(shí)例
第三方短信發(fā)送平臺(tái)有很多種,各個(gè)平臺(tái)有各自的優(yōu)缺點(diǎn),在選擇的時(shí)候可以根據(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的一個(gè)方面,是個(gè)通常的概念,它有多種解釋,這概念是說你不用創(chuàng)建對(duì)象,而只需要描述它如何被創(chuàng)建,需要的朋友可以參考下2023-07-07Java異常中toString()和getMessage()區(qū)別
在java異常體系中,要打印異常信息,可以通過:e.getMessage() 、 e.toString() e.printStackTrace() 等方法打印,本文主要介紹了Java異常中toString()和getMessage()區(qū)別,具有一定的參考價(jià)值,感興趣的可以了解一下2024-01-01Java中HashMap和Hashtable的區(qū)別淺析
這篇文章主要介紹了Java中HashMap和Hashtable的區(qū)別淺析,本文總結(jié)了6條它們之間的不同之處,需要的朋友可以參考下2015-03-03Java利用MD5加鹽實(shí)現(xiàn)對(duì)密碼進(jìn)行加密處理
在開發(fā)的時(shí)候,有一些敏感信息是不能直接通過明白直接保存到數(shù)據(jù)庫的。最經(jīng)典的就是密碼了。如果直接把密碼以明文的形式入庫,不僅會(huì)泄露用戶的隱私,對(duì)系統(tǒng)也是極其的不厲。本文就來和大家介紹一下如何對(duì)密碼進(jìn)行加密處理,感興趣的可以了解一下2023-02-02idea運(yùn)行tomcat報(bào)錯(cuò)找不到catalina.bat,系統(tǒng)找不到指定的文件問題
這篇文章主要介紹了idea運(yùn)行tomcat報(bào)錯(cuò)找不到catalina.bat,系統(tǒng)找不到指定的文件問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,2023-11-11java正則表達(dá)式之Pattern與Matcher類詳解
這篇文章主要給大家介紹了關(guān)于java正則表達(dá)式之Pattern與Matcher類的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09