一對一級聯(lián)關(guān)系在現(xiàn)實生活中是十分常見的,例如一個大學(xué)生只有一個學(xué)號,一個學(xué)號只屬于一個學(xué)生。同樣,人與身份證也是一對一的級聯(lián)關(guān)系。
在 MyBatis 中,通過 <resultMap> 元素的子元素 <association> 處理一對一級聯(lián)關(guān)系。示例代碼如下。
<association property="studentCard" column="cardId" javaType="net.biancheng.po.StudentCard" select="net.biancheng.mapper.StudentCardMapper.selectStuCardById" />
在 <association> 元素中通常使用以下屬性。
一對多的關(guān)系,如角色和用戶的關(guān)系。通俗的理解就是,一家軟件公司會存在許多軟件工程師,公司和軟件工程師就是一對多的關(guān)系。
一對一的關(guān)系。每個軟件工程師都有一個編號(ID),這是他在公司的標識,它與工程師是一對一的關(guān)系。
多對多的關(guān)系,有些公司一個角色可以對應(yīng)多個用戶,但是一個用戶可以兼任多個角色。通俗的說,一個人既可以是總經(jīng)理,同時也是技術(shù)總監(jiān),而技術(shù)總監(jiān)這個職位可以對應(yīng)多個人,這就是多對多的關(guān)系。
實際應(yīng)用中,由于多對多的關(guān)系比較復(fù)雜,會增加理解和關(guān)聯(lián)的復(fù)雜度,所以應(yīng)用較少。推薦的方法是,用一對多的關(guān)系把它分解為雙向關(guān)系,以降低關(guān)系的復(fù)雜度,簡化程序。具體操作方法可以參考《MyBatis多對多關(guān)聯(lián)查詢》一節(jié)。
級聯(lián)的優(yōu)點是獲取關(guān)聯(lián)數(shù)據(jù)十分便捷。但是級聯(lián)過多會增加系統(tǒng)的復(fù)雜度,同時降低系統(tǒng)的性能,此增彼減。所以記錄超過 3 層時,就不要考慮使用級聯(lián)了,因為這樣會造成多個對象的關(guān)聯(lián),導(dǎo)致系統(tǒng)的耦合、負載和難以維護。
本教程分為 3 節(jié)對 MyBatis 的級聯(lián)關(guān)系進行詳細講解,小伙伴們請點擊下方鏈接閱讀學(xué)習。
property:指定映射到實體類的對象屬性。
column:指定表中對應(yīng)的字段(即查詢返回的列名)。
javaType:指定映射到實體對象屬性的類型。
select:指定引入嵌套查詢的子 SQL 語句,該屬性用于關(guān)聯(lián)映射中的嵌套查詢。
一對一關(guān)聯(lián)查詢可采用以下兩種方式:
單步查詢,通過關(guān)聯(lián)查詢實現(xiàn)
分步查詢,通過兩次或多次查詢,為一對一關(guān)系的實體 Bean 賦值
下面以學(xué)生和學(xué)號為例講解一對一關(guān)聯(lián)查詢的處理過程。
創(chuàng)建 student(學(xué)生)和 studentcard(學(xué)號)數(shù)據(jù)表,SQL 語句如下。
CREATE TABLE `student` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, `sex` tinyint(4) DEFAULT NULL, `cardId` int(20) DEFAULT NULL, PRIMARY KEY (`id`), KEY `cardId` (`cardId`), CONSTRAINT `student_ibfk_1` FOREIGN KEY (`cardId`) REFERENCES `studentcard` (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8; insert into `student`(`id`,`name`,`sex`,`cardId`) values (1,'C語言中文網(wǎng)',0,1),(2,'編程幫',0,2),(3,'趙小紅',1,3),(4,'李曉明',0,4),(5,'李紫薇',1,5),(6,'錢百百',0,NULL); DROP TABLE IF EXISTS `studentcard`; CREATE TABLE `studentcard` ( `id` int(20) NOT NULL AUTO_INCREMENT, `studentId` int(20) DEFAULT NULL, `startDate` date DEFAULT NULL, `endDate` date DEFAULT NULL, PRIMARY KEY (`id`), KEY `studentId` (`studentId`) ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8; insert into `studentcard`(`id`,`studentId`,`startDate`,`endDate`) values (1,20200311,'2021-03-01','2021-03-11'),(2,20200314,'2021-03-01','2021-03-11'),(3,20200709,'2021-03-01','2021-03-11'),(4,20200508,'2021-03-01','2021-03-11'),(5,20207820,'2021-03-01','2021-03-11');
在 myBatisDemo 應(yīng)用的 net.biancheng.po 包下創(chuàng)建數(shù)據(jù)表對應(yīng)的持久化類 Student 和 StudentCard。
Student 的代碼如下:
package net.biancheng.po; public class Student { private int id; private String name; private int sex; private StudentCard studentCard; /*省略setter和getter方法*/ @Override public String toString() { return "Student [id=" + id + ", name=" + name + ", sex=" + sex + ", studentCard=" + studentCard + "]"; } }
StudentCard 的代碼如下:
package net.biancheng.po; import java.util.Date; public class StudentCard { private int id; private int studentId; private Date startDate; private Date endDate; /*省略setter和getter方法*/ @Override public String toString() { return "StudentCard [id=" + id + ", studentId=" + studentId + "]"; } }
新建 StudentCardMapper 類,代碼如下。
package net.biancheng.mapper; import net.biancheng.po.StudentCard; public interface StudentCardMapper { public StudentCard selectStuCardById(int id); }
StudentCardMapper.xml 對應(yīng)映射 SQL 語句代碼如下。
<mapper namespace="net.biancheng.mapper.StudentCardMapper"> <select id="selectStuCardById" resultType="net.biancheng.po.StudentCard"> SELECT * FROM studentCard WHERE id = #{id} </select> </mapper>
StudentMapper 類方法代碼如下。
package net.biancheng.mapper; import net.biancheng.po.Student; public interface StudentMapper { public Student selectStuById1(int id); public Student selectStuById2(int id); }
StudentMapper.xml 代碼如下。
<mapper namespace="net.biancheng.mapper.StudentMapper"> <!-- 一對一根據(jù)id查詢學(xué)生信息:級聯(lián)查詢的第一種方法(嵌套查詢,執(zhí)行兩個SQL語句) --> <resultMap type="net.biancheng.po.Student" id="cardAndStu1"> <id property="id" column="id" /> <result property="name" column="name" /> <result property="sex" column="sex" /> <!-- 一對一級聯(lián)查詢 --> <association property="studentCard" column="cardId" javaType="net.biancheng.po.StudentCard" select="net.biancheng.mapper.StudentCardMapper.selectStuCardById" /> </resultMap> <select id="selectStuById1" parameterType="Integer" resultMap="cardAndStu1"> select * from student where id=#{id} </select> </mapper>
測試代碼如下。
public class Test { public static void main(String[] args) throws IOException { InputStream config = Resources.getResourceAsStream("mybatis-config.xml"); SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(config); SqlSession ss = ssf.openSession(); Student stu = ss.getMapper(StudentMapper.class).selectStuById1(2); System.out.println(stu); } }
運行結(jié)果如下。
DEBUG [main] - ==> Preparing: select * from student where id=? DEBUG [main] - ==> Parameters: 2(Integer) DEBUG [main] - ====> Preparing: SELECT * FROM studentCard WHERE id = ? DEBUG [main] - ====> Parameters: 2(Integer) DEBUG [main] - <==== Total: 1 DEBUG [main] - <== Total: 1 Student [id=2, name=編程幫, sex=0, studentCard=StudentCard [id=2, studentId=20200314]]
在 StudentMapper.xml 中添加以下代碼。
<resultMap type="net.biancheng.po.Student" id="cardAndStu2"> <id property="id" column="id" /> <result property="name" column="name" /> <result property="sex" column="sex" /> <!-- 一對一級聯(lián)查詢 --> <association property="studentCard" javaType="net.biancheng.po.StudentCard"> <id property="id" column="id" /> <result property="studentId" column="studentId" /> </association> </resultMap> <select id="selectStuById2" parameterType="Integer" resultMap="cardAndStu2"> SELECT s.*,sc.studentId FROM student s,studentCard sc WHERE s.cardId = sc.id AND s.id=#{id} </select>
在 StudentMapper 中添加以下方法。
public Student selectStuById2(int id);
運行結(jié)果如下。
DEBUG [main] - ==> Preparing: SELECT s.*,sc.studentId FROM student s,studentCard sc WHERE s.cardId = sc.id AND s.id=? DEBUG [main] - ==> Parameters: 2(Integer) DEBUG [main] - <== Total: 1 Student [id=2, name=編程幫, sex=0, studentCard=StudentCard [id=2, studentId=20200314]]