4.1 关联关系概述
数据表关系:
- 一对一:在任意一方引入对方主键作为外键。
- 一对多:在“多”的一方添加“一”的一方的主键作为外键。
- 多对多:产生中间关系表,引入两张表的主键作为外键,两个主键成为联合主键或使用新的字段作为主键。
对象之间关系
- 一对一的关系:在本类中定义对方类型的对象,比如A类中定义B类类型的属性b、在B类中定义A类类型的属性a。
- 一对多的关系:一个A类类型对应多个B类类型的情况,需要在A类中以集合的方式引入B类类型的对象,在B类中定义A类类型的属性a。
- 多对多的关系:在A类中定义B类类型的集合,在B类中定义A类类型的集合。
4.2 MyBatis中的关联关系
4.2.1 一对一
通过<association>
元素处理一对一关系。
可以配置以下属性:
- property:指定映射到的实体类对象属性,与表字段一一对应。
- column:指定表中对应的字段。
- javaType:指定映射到实体对象属性的类型。
- select:指定引入嵌套查询的子SQL语句,用于关联映射中的嵌套查询。
- fetchType:指定在关联查询时是否启用延迟加载,有lazy和eager两个属性值,默认值为lazy(默认关联映射延迟加载)。
示例4-1
查询学生及其关联的学生证信息是先通过查询学生表中的主键来获取学生信息,然后通过表中的外键来获取学生证表中的学生证号信息。
- 创建数据表。
use db_mybatis;
create table tb_studentidcard(
id int primary key auto_increment,
CODE varchar(8)
);
insert into tb_studentidcard(CODE) values('18030128');
insert into tb_studentidcard(CODE) values('18030135');
create table tb_student(
id int primary key auto_increment,
name varchar(32),
sex char(1),
card_id int unique,
foreign key (card_id) references tb_studentidcard(id)
);
insert into tb_student(name,sex,card_id) values('limin','f',1);
insert into tb_student(name,sex,card_id) values('jack','m',2);
- 创建项目,导包,工具类,核心配置文件。
- 在com.ssm.po下创建持久化类:StudentIdCard和Student
StudentIdCard
package com.ssm.po;
public class StudentIdCard {
private Integer id;
private String code;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String toString() {
return "StudentIdCard{" +
"id=" + id +
", code='" + code + '\'' +
'}';
}
}
Student
package com.ssm.po;
public class Student {
private Integer id;
private String name;
private String sex;
private StudentIdCard studentIdCard;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public StudentIdCard getStudentIdCard() {
return studentIdCard;
}
public void setStudentIdCard(StudentIdCard studentIdCard) {
this.studentIdCard = studentIdCard;
}
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", sex='" + sex + '\'' +
", studentIdCard=" + studentIdCard +
'}';
}
}
- 在com.ssm.mapper中创建StudentIdCardMapper.xml和StudentMapper.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.StudentIdCardMapper">
<select id="findStudentIdCardById" parameterType="Integer" resultType="StudentIdCard">
select * from tb_studentidcard where id=#{id}
</select>
</mapper>
<?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="findStudentById" parameterType="Integer" resultType="StudentIdCardWithStudentResult">
select * from tb_student where id=#{id}
</select>
<resultMap id="StudentIdCardWithStudentResult" type="Student">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="sex" column="sex"/>
<!-- 一对一,association使用select属性引入另一条SQL语句-->
<association property="StudentIdCard" column="card_id" javaType="StudentIdCard"
select="com.ssm.mapper.StudentIdCardMapper.findStudentIdCardById"/>
</resultMap>
</mapper>
嵌套查询的方法是先执行一个简单的SQL语句,然后在进行结果映射时将关联对象在association元素中使用select属性执行另一条SQL语句(StudentIdCardMapper中的SQL)
- 核心配置中引入Mapper文件并定义别名
<?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"/>
<!-- 1.配置环境,默认的环境id为mysql-->
<environments default="mysql">
<!-- 1.2 配置id为mysql的数据库环境-->
<environment id="mysql">
<!-- 使用JDBC事务管理-->
<transactionManager type="JDBC"></transactionManager>
<!-- 数据库连接池-->
<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>
<!-- 2.配置mapper位置-->
<mappers>
<mapper resource="com/ssm/mapper/StudentIdCardMapper.xml"/>
<mapper resource="com/ssm/mapper/StudentMapper.xml"/>
</mappers>
</configuration>
- 创建测试类
package com.ssm.test;
import com.ssm.po.Student;
import com.ssm.util.MyBatisUtil;
import org.apache.ibatis.session.SqlSession;
public class findStudentByIdTest {
public static void main(String[] args){
SqlSession sqlSession= MyBatisUtil.getSession();
// 使用嵌套查询
Student student=sqlSession.selectOne("com.ssm.mapper.StudentMapper.findStudentById",1);
System.out.println(student.toString());
sqlSession.close();
}
}
4.2.2 一对多
<collection>
元素用于处理一对多关系。
示例4-2
- 创建两个数据表
create table tb_banji(
id int primary key auto_increment,
name varchar(32)
);
insert into tb_banji values(1,'16软件工程1班');
insert into tb_banji values(2,'16软件工程2班');
create table tb_student(
id int primary key auto_increment,
name varchar(32),
sex char(1),
banji_id int,
foreign key (banji_id) references tb_banji(id)
);
insert into tb_student values(1,'张三','m',1);
insert into tb_student values(2,'李四','f',1);
insert into tb_student values(3,'王五','m',2);
- 创建持久化类
package com.ssm.po;
import java.util.List;
public class Banji {
private Integer id;
private String name;
private List<Student> studentList;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Student> getStudentList() {
return studentList;
}
public void setStudentList(List<Student> studentList) {
this.studentList = studentList;
}
@Override
public String toString() {
return "Banji{" +
"id=" + id +
", name='" + name + '\'' +
", studentList=" + studentList +
'}';
}
}
package com.ssm.po;
public class Student {
private Integer id;
private String name;
private String sex;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", sex='" + sex + '\'' +
'}';
}
}
- 创建BanjiMapper.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.BanjiMapper">
<!-- 一对多:查看某以班级及其关联学生信息-->
<!-- 注意:若关联查询出的列名相同,要以别名区分-->
<select id="findBanjiWithStudent" parameterType="Integer" resultMap="BanjiWithStudentResult">
select b.*,s.id as student id,s.name from tb_banji b,tb_student s where b.id=s.banji_id and b.id=#{id}
</select>
<resultMap id="BanjiWithStudentResult" type="Banji">
<id property="id" column="id"/>
<result property="name" column="name"/>
<!-- 一对多关系映射-->
<collection property="studentList" ofType="Student">
<id property="id" column="student_id"/>
<result property="name" column="name"/>
<result property="sex" column="sex"/>
</collection>
</resultMap>
</mapper>
- 配置到核心文件
<mapper resource="com/ssm/mapper/BanjiMapper.xml"/>
- 编写测试类
package com.ssm.test;
import com.ssm.po.Banji;
import com.ssm.util.MyBatisUtil;
import org.apache.ibatis.session.SqlSession;
public class findBanjiTest {
public static void main(String[] args){
SqlSession sqlSession= MyBatisUtil.getSession();
Banji banji=sqlSession.selectOne("com.ssm.mapper.BanjiMapper.findBanjiWithStudent",1);
System.out.println(banji.toString());
sqlSession.close();
}
}
4.2.3 多对多
示例4-3
- 创建数据表
create table tb_course(
id int primary key auto_increment,
name varchar(32),
code varchar(32)
);
INSERT into tb_course values (1,'java','08113226');
insert into tb_course values (2,'python','08113228');
create table tb_elecctiveCourse(
id int primary key auto_increment,
student_id int,
course_id int,
foreign key(student_id) references tb_student(id),
foreign key(course_id) references tb_course(id)
);
insert into tb_elecctiveCourse values (1,1,1);
insert into tb_elecctiveCourse values (2,1,2);
insert into tb_elecctiveCourse values (3,2,2);
- 创建持久化类Course
package com.ssm.po;
import java.util.List;
public class Course {
private Integer id;
private String name;
private String code;
private List<Student> studentList;//与学生集合的关联属性
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public List<Student> getStudentList() {
return studentList;
}
public void setStudentList(List<Student> studentList) {
this.studentList = studentList;
}
@Override
public String toString() {
return "Course{" +
"id=" + id +
", name='" + name + '\'' +
", code='" + code + '\'' +
", studentList=" + studentList +
'}';
}
}
在Student中添加课程集合属性。
package com.ssm.po;
import java.util.List;
public class Student {
private Integer id;
private String name;
private String sex;
private List<Course> courseList;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public List<Course> getCourseList() {
return courseList;
}
public void setCourseList(List<Course> courseList) {
this.courseList = courseList;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", sex='" + sex + '\'' +
", courseList=" + courseList +
'}';
}
}
- 创建映射文件CourseMapper和StudentMapper
<?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.CourseMapper">
<!-- 多对多 通过一条sql来返回预期的特殊类型-->
<select id="findCourseWithStudent" parameterType="Integer" resultMap="CourseWithStudentResult">
select * from tb_course where id=#{id}
</select>
<resultMap id="CourseWithStudentResult" type="Course">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="code" column="code"/>
<collection property="studentList" column="id" ofType="Student"
select="com.ssm.mapper.StudentMapper.findStudentById">
</collection>
</resultMap>
</mapper>
<?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="findStudentById" parameterType="Integer" resultType="Student">
select * from tb_student where id in(
select student_id from tb_electivecourse where course_id=#{id}
)
</select>
</mapper>
- 配置核心文件
<mapper resource="com/ssm/mapper/CourseMapper.xml"/>
<mapper resource="com/ssm/mapper/StudentMapper.xml"/>
- 编写测试类
package com.ssm.test;
import com.ssm.po.Course;
import com.ssm.util.MyBatisUtil;
import org.apache.ibatis.session.SqlSession;
public class findCourseByIdTest {
public static void main(String[] args){
SqlSession sqlSession= MyBatisUtil.getSession();
Course course=sqlSession.selectOne("com.ssm.mapper.CourseMapper.findCourseWithStudent",1);
System.out.println(course);
sqlSession.close();
}
}