级联关系是一个数据库实体的概念,有 3 种级联关系,分别是一对一级联、一对多级联以及多对多级联。例如,一个角色可以分配给多个用户,也可以只分配给一个用户。大部分场景下,我们都需要获取角色信息和用户信息,所以会经常遇见以下 SQL。
SELECT r.*,u.* FROM t_role r
INNER JOIN t_user_role ur ON r.id = ur.id
INNER JOIN t_user u ON ur.user_id = u.id
WHERE r.id = #{id}
在级联中存在 3 种对应关系。
- 一对多的关系,如角色和用户的关系。通俗的理解就是,一家软件公司会存在许多软件工程师,公司和软件工程师就是一对多的关系。
- 一对一的关系。每个软件工程师都有一个编号(ID),这是他在公司的标识,它与工程师是一对一的关系。
- 多对多的关系,有些公司一个角色可以对应多个用户,但是一个用户可以兼任多个角色。通俗的说,一个人既可以是总经理,同时也是技术总监,而技术总监这个职位可以对应多个人,这就是多对多的关系。
MyBatis一对一关联查询
一对一级联关系在现实生活中是十分常见的,例如一个大学生只有一个学号,一个学号只属于一个学生。同样,人与身份证也是一对一的级联关系。
在 MyBatis 中,通过 <resultMap> 元素的子元素 <association> 处理一对一级联关系。示例代码如下。
<association property="studentCard" column="cardId"
javaType="net.cc.po.StudentCard"
select="net.cc.mapper.StudentCardMapper.selectStuCardById" />
在 <association> 元素中通常使用以下属性。
- property:指定映射到实体类的对象属性。
- column:指定表中对应的字段(即查询返回的列名)。
- javaType:指定映射到实体对象属性的类型。
- select:指定引入嵌套查询的子 SQL 语句,该属性用于关联映射中的嵌套查询。
一对一关联查询可采用以下两种方式:
- 单步查询,通过关联查询实现
- 分步查询,通过两次或多次查询,为一对一关系的实体 Bean 赋值
示例:下面以学生和学号为例讲解一对一关联查询的处理过程。
1.创建数据表
创建 student(学生)和 studentcard(学号)数据表,此处省略。
2.创建持久化类
学生类
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 + "]";
}
}
学生学号类
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.xml 对应映射 SQL 语句代码如下。
<mapper namespace="net.cc.mapper.StudentCardMapper">
<select id="selectStuCardById"
resultType="net.cc.po.StudentCard">
SELECT * FROM studentCard WHERE id = #{id}
</select>
</mapper>
StudentMapper.xml 代码如下
<mapper namespace="net.cc.mapper.StudentMapper">
<!-- 一对一根据id查询学生信息:级联查询的第一种方法(嵌套查询,执行两个SQL语句) -->
<resultMap type="net.cc.po.Student" id="cardAndStu1">
<id property="id" column="id" />
<result property="name" column="name" />
<result property="sex" column="sex" />
<!-- 一对一级联查询 -->
<association property="studentCard" column="cardId"
javaType="net.cc.po.StudentCard"
select="net.cc.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);
}
}
运行结果如下
单步查询
在 StudentMapper.xml 中添加以下代码
<resultMap type="net.cc.po.Student" id="cardAndStu2">
<id property="id" column="id" />
<result property="name" column="name" />
<result property="sex" column="sex" />
<!-- 一对一级联查询 -->
<association property="studentCard"
javaType="net.cc.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>
运行结果如下