一對一級聯(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]]