关联关系
在关系型数据库中,多表之间存在着三种关联关系,分别为一对一、一对多和多对多。
一对一:在任意一方引入对方主键作为外键。
一对多:在“多”的一方,,添加“一”的一方的主键作为外键。
多对多:产生中间关系表,引入两张表的主键作为外键,两个主键成为联合主键或使用新的字段作为主键。
1、一对一
例如一个学生对应一个学号,一个学号也只是一个学生。
在前面的学习中< resultMap>元素中,包含了一个< association>子元素,MyBatis就是通过该元素来处理一对关联关系的。
通过下面例子展开:
查询学生及其关联的学生证信息是先通过查询学生表中的主键来获学生信息,然后通过表中的外键,来获取学生证表中的学生证号信息。
(1)创建数据表。
tb_studentsid
表
tb_student
表(id自增)
并在tb_student
表中添加外键
(2)在 Eclipse中创建一个名为 chapter09的Web项目,然后引入相关JAR包、 MybatisUtils工具类以及mybatis- config. xm核心配置文件。
src目录下
(3)在项目的 com.ssm.po包下创建持久化类StudentIdCard和Student;
并创建setter和getter方法,以及tostring()方法。
(4)在 com. ssm. mapper包中,创建学生证映射文件StudentsIdMapper.xml和学生映射文件StudentMapper.xml,并在两个映射文件中编写一对一关联映射查询的配置信息;
<!--StudentsIdMapper.xml-->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ssm.mapper.StudentsIdMapper">
<select id="findStuIdById" parameterType="Integer" resultType="StudentsId">
select * from tb_studentsid where id=#{id}
</select>
</mapper>
StudentMapper.xml中有两种写法
(1)
< association property="studentsId" column="card_id" javaType="StudentsId" select="com.ssm.mapper.StudentsIdMapper.findStuIdById" />
(2)
< association property="studentsId" javaType="StudentsId" fetchType="eager">
< id property="id" column="cart_id" />
< id property="code" column="code" />
</ association >
其中association
元素的属性:
property:指定映射到实体类对象属性,与表字段以一对应
column:指定表中对应的字段
javaTape:指定映射到实体对象属性的类型
select:指定引入嵌套查询的子SQL语句,用于关联映射中的嵌套查询
fetchType:指定在关联查询时是否启用延迟加载,有lazy和eager两个属性值,默认lazy。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ssm.mapper.StudentMapper">
<!--
<select id="findStuById" parameterType="Integer" resultMap="StuIdWithStu">
select * from tb_student where id=#{id}
</select>
<resultMap type="Student" id="StuIdWithStu">
<id property="id" column="id" />
<result property="name" column="name" />
<result property="sex" column="sex" />
<association property="studentsId" column="card_id" javaType="StudentsId" select="com.ssm.mapper.StudentsIdMapper.findStuIdById" />
</resultMap>
-->
<!--嵌套结果,通过嵌套结果映射来处理重复的联合结果的子集-->
<select id="findStuById2" parameterType="Integer" resultMap="StuWithStuId">
select s.*,sid.code
from tb_studentsid sid,tb_student s
where s.card_id=sid.id and s.id=#{id}
</select>
<resultMap type="Student" id="StuWithStuId">
<id property="id" column="id" />
<result property="name" column="name" />
<result property="sex" column="sex" />
<association property="studentsId" javaType="StudentsId" fetchType="eager">
<id property="id" column="cart_id" />
<id property="code" column="code" />
</association>
</resultMap>
</mapper>
(5)在核心配置文件mybatis-config. xml中,引入Mapper映射文件并定义别名;
设置typeAliases时要注意该元素所在的位置,写错地方会导致无法生效
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="db.properties" />
<settings>
<!-- 打开延迟加载开关 -->
<setting name="lazyLoadingEnabled" value="true" />
<!-- 将积极加载改为消极加载,按需加载 -->
<setting name="aggressiveLazyLoading" value="false" />
</settings>
<!-- 使用扫描包的形式定义别名 -->
<typeAliases>
<package name="com.ssm.po" />
</typeAliases>
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/ssm/mapper/StudentMapper.xml" />
<mapper resource="com/ssm/mapper/StudentsIdMapper.xml" />
</mappers>
</configuration>
(6)在com. ssm.test包中,创建测试类 MybatisAssociatedTest,并在类中编写测试方法 findStudentByldTest();
@Test
public void findStuByIdTest(){
SqlSession session=MybatisUtil.getSession();
Student stu=session.selectOne("com.ssm.mapper.StudentMapper.findStuById2",1);
System.out.println(stu.toString());
session.close();
}
2、一对多
例如一个班级有多个学生,也即多个学生属性一个班级。
<resultMap>
元素中,包含了一个<collection>
子元素,MyBatis就是通过该元素来处理一对多关联关系的。
<collection>
元素属性包含一个ofType属性,与javaType属性对应,用于指定实体对象中集合类属性所包含的元素类型。
(1)在db_mybatis数据库中,创建两个数据表,分别为 tb_class和 tb_students,同时在表中预先插入几条数据;
tb_class
表
tb_students
表
添加外键class_id
(2)在 com.ssm. po包中,创建持久化类Sclass和Students,并在两个类中定义相关属性和方法;
(3)在 com. ssm. mapper包中,创建班级实体映射文件SclassMapper.xml,并在文件中编写一对多关联映射查询的配置;
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ssm.mapper.SclassMapper">
<!-- 一对多,查看某一班级及其相关学生信息 -->
<select id="findClassWithStu" parameterType="Integer" resultMap="ClassWithStu">
select c.*,s.id as students_id,s.name,s.sex
from tb_class c,tb_students s
where c.id=s.class_id and c.id=#{id}
</select>
<resultMap type="Sclass" id="ClassWithStu">
<id property="id" column="id" />
<result property="cname" column="cname" />
<collection property="studentList" ofType="Students">
<id property="id" column="students_id" />
<result property="name" column="name" />
<result property="sex" column="sex" />
</collection>
</resultMap>
</mapper>
(4)将映射文件SclassMapper.xml的路径配置到核心配置文件mybatis-config. xml中;
<mapper resource="com/ssm/mapper/SclassMapper.xml" />
将代码放到Mappers元素内
(5)在测试类 MybatisAssociatedTest中,编写测试方法 findBanjiTest();
@Test
public void findStuClass(){
SqlSession session =MybatisUtil.getSession();
Sclass sclass=session.selectOne("com.ssm.mapper.SclassMapper.findClassWithStu",1);
System.out.println(sclass.toString());
session.close();
}
DEBUG [main]- ==> Preparing: select c.*,s.id as students_id,s.name,s.sex from tb_class c,tb_students s where c.id=s.class_id and c.id=?
DEBUG [main]- ==> Parameters: 1(Integer)
DEBUG [main]- <== Total: 3
Sclass [id=1, cname=软件1班, studentList=[Students [id=1, name=韩琪轩, sex=男], Students [id=2, name=张三, sex=女], Students [id=3, name=李四, sex=男]]]
3、多对多
在实际项目开发中,多对多的关联关系也是非常常见的。以学生和课程为例,一个学生可以选修多门课程,而一门课程又可以被多个学生选修,学生和课程就属于多对多的关联关系。在多对多关系中,通过中间表来实现关联。
下面是一个案例需求:
(1)创建数据表。
(2)在com.ssm.po包中,创建持久化类Department以及Employee,并在类中定义相关属性和方法;
(3)在 com. ssm. mapper包中,创建部门实体映射文件
DepartmentMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ssm.mapper.DepartmentMapper">
<select id="finddepartmentwithemployee" parameterType="Integer" resultMap="findDep">
select d.*,e.id as eid,e.ename,e.sex
from t_employee e,t_department d,t_contact c
where c.depart_id=d.id
and c.employee_id=e.id and d.id=#{id}
</select>
<resultMap type="Department" id="findDep">
<id property="id" column="id" />
<result property="dname" column="dname" />
<result property="code" column="code" />
<collection property="employeelist" ofType="Employee">
<id property="id" column="eid" />
<result property="ename" column="ename" />
<result property="sex" column="sex" />
</collection>
</resultMap>
</mapper>
EmployeeMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ssm.mapper.EmployeeMapper">
<select id="findEmployee" parameterType="Integer" resultType="Employee">
select * from t_employee where id in(
select employee_id from t_contact where depart_id=#{id}
)
</select>
</mapper>
(4)将新创建的映射文件DepartmentMapper.xml和EmployeeMapper.xml的文件路径配置到核心配置文件 mybatis-config.xml中;
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 属性 -->
<properties resource="db.properties" />
<typeAliases>
<package name="com.ssm.po" />
</typeAliases>
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/ssm/mapper/DepartmentMapper.xml" />
<mapper resource="com/ssm/mapper/EmployeeMapper.xml" />
</mappers>
</configuration>
(5)在测试类 MybatisAssociatedTest中,编写多对多关联查询的测试方法 findDepartTest();
package com.ssm.test;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import com.ssm.po.Department;
import com.ssm.util.MyBatisUtil;
public class MyBatisTest {
@Test
public void findDepartTest(){
SqlSession session=MyBatisUtil.getSession();
Department department=session.selectOne("com.ssm.mapper.DepartmentMapper.finddepartmentwithemployee",1);
System.out.println(department.toString());
session.close();
}
}
DEBUG [main]- ==> Preparing: select d.*,e.id as eid,e.ename,e.sex from t_employee e,t_department d,t_contact c where c.depart_id=d.id and c.employee_id=e.id and d.id=?
DEBUG [main]- ==> Parameters: 1(Integer)
DEBUG [main]- <== Total: 2
Department [id=1, dname=技术部门, code=1001, employeelist=[Employee [id=1, ename=张三, sex=男], Employee [id=2, ename=李四, sex=女]]]
MyBatis中的关联查询操作在实际开发中非常普遍,熟练掌握这三种关联查询方式有助于提高项目的开发效率。