MyBatis中的关联关系
Mybatis的三种关联关系:一对一、一对多、多对多。下面代码均可跑出正确想要的结果
【一对一】
【知识点】
在现实生活中,一对一关联关系是十分常见的。例如,一个学生只有一本学生证,同时一本学生证也只对应一个学生。那么MyBatis是怎么处理这种一对一关联关系的呢?
在< resultMap >元素中包含一个< association >子元素,MyBatis就是通过该元素来处理一对一关联关系的。在< associatio n>元素中,通常可以配置以下属性:
- property:指定映射到的实体类对象属性,与表字段一一对应。
- column:指定表中对应的字段。
- javaType:指定映射到实体对象属性的类型。
- select:指定引入嵌套查询的子SQL语句,用于关联映射中的嵌套查询。
- fetchType:指定在关联查询时是否启用延迟加载,有lazy和eager两个属性值,默认值为lazy(默认关联映射延迟加载)。
< association>元素有如下两种配置方式。
< !- - 方式一:嵌套查询-->
< association property="card" column="card_id" javaType="com.Jorya.model.StudentIdCard"
select="com.Jorya.mapper.StudentIdCardMapper.findCodeById"/>
<!-- 方式二: 嵌套结果-->
<association property="card" javaType="com.Jorya.Model.StudentIdCard">
<id property="id" column="card_id"/>
<result property="code" column="code"/>
</association>
MyBatis在映射文件中加载关联关系对象主要通过两种方式:嵌套查询和嵌套结果。嵌套查询是指通过执行另一条SQL映射语句来返回预期的复杂类型;嵌套结果是使用嵌套结果映射来处理重复的联合结果的子集。
一、【一对一代码示例】(oneToOne)
接下来以学生和学生证之间的一对一关联关系为例进一步进行讲解。查询学生及其关联的学生证信息是先通过查询学生表中的主键来获取学生信息,然后通过表中的外键来获取学生证表中的学生证号信息。其具体实现步骤如下。
1.1 创建数据表。
在db_mybatis数据库中分别创建名为otm_studentidcard和otm_student的数据表,同时插入几条数据。其执行的SQL语句如下所示
#使用数据库 mybatis_Jorya
USE mybatis_Jorya;
#创建一个名称为oto_studentidcard 的表
CREATE TABLE oto_studentidcard(
id INT PRIMARY KEY AUTO_INCREMENT,
CODE VARCHAR (8)
);
#插入两条数据
INSERT INTO oto_studentidcard (CODE) VALUES ('W1900000');
INSERT INTO oto_studentidcard (CODE) VALUES ('W1800000');
INSERT INTO oto_studentidcard (CODE) VALUES ('W1800001');
#创建一个名称为oto__student的表(暂时添加少量字段)
CREATE TABLE oto_student (
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(32),
sex CHAR(1),
card_id INT UNIQUE,
FOREIGN KEY (card_id) REFERENCES oto_studentidcard (id)
);
#插入两条数据
INSERT INTO oto_student (name, sex, card_id) VALUES('Jorya','W',1);
INSERT INTO oto_student (name, sex, card_id) VALUES('jack', 'm',2);
1.2 配置文件
在Eclipse中创建一个名为com.Jorya.mybatis的Web项目,然后引入相关JAR包、MybatisUtils工具类以及mybatis-config.xml核心配置文件
1.2.1 jar包导入
1.2.2 MybatisUtils工具类
package com.Jorya.util;
import java.io.IOException;
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class MybatisUtil {
private static SqlSessionFactory sqlSessionFactory=null;
static {
try {
String resource="mybaits_config.xml";
InputStream inputStream=Resources.getResourceAsStream(resource);
sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession getSession() {
return sqlSessionFactory.openSession();
}
}
1.2.3 mybatis-config.xml核心配置文件
< mapper>标签下的resource资源就是所需引用的xml配置文件(如果不需要那个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"/>
<settings>
<!-- 打开延迟加载开关 -->
<setting name="lazyLoadingEnabled" value="True"/>
<!--将积极加载改为消息加载,即按需加载 -->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
<typeAliases>
<package name="com.Jorya.model"/>
</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/Jorya/mapper/StudentIdCardMapper.xml"/>
<mapper resource="com/Jorya/mapper/StudentMapper.xml"/>
</mappers>
</configuration>
在上述核心配置文件中,首先引入了数据库连接的配置文件,然后使用扫描包的形式自定义别名,接下来进行环境的配置,最后配置了Mapper映射文件的位置信息。
1.2.4 db.propertise配置文件
//在老版本中 jdbc.driver是com.mysql.jdbc.Driver 好多人会出现连接不上数据库的原因就是这个,新版本中 jdbc.driver=com.mysql.cj.jdbc.Driver
如果有下面的报错 需要加上 ?characterEncoding=utf-8&serverTimezone=UTC&useSSL=false
Cause: java.sql.SQLException: The server time zone value ‘�й���ʱ��’ is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the serverTimezone configuration property) to use a more specifc time zone value if you want to utilize time zone support.
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis_jorya?characterEncoding=utf-8&serverTimezone=UTC&useSSL=false
jdbc.username=root
jdbc.password=root
1.2.5 log4j.propertise配置文件(日志文件)
# Global logging configuration
log4j.rootLogger = ERROR, stdout
# Mybatis logging configuration...
log4j.logger.com.Jorya=DEBUG
Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
1.3 持久化类
在项目的com.Jorya.model包下创建持久化类:学生证类StudentIdCard和学生类Student,编辑后的代码如 1.3.1和 1.3.2所示。
1.3.1 StudentIdCard.java
package com.Jorya.model;
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 +"]";
}
}
1.3.2 Student.java
package com.Jorya.model;
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 "StudentIdCard [id="+id+",name=" + name +",sex="+sex+",studentIdCard="+studentIdCard+"]";
}
}
1.4 映射文件
在com.Jorya.mapper包中创建学生证映射文件StudentIdCardMapper.xml和学生映射文件StudentMapper.xml,并在两个映射文件中编写一对一关联映射查询的配置信息,如 1.41和 1.42所示。
1.41 StudentIdCardMapper.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.Jorya.mapper.StudentIdCardMapper">
<select id="findStudentIdCardById" parameterType="Integer" resultType="StudentIdCard">
select * from oto_studentidcard where id=#{id}
</select>
</mapper>
1.42 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.Jorya.mapper.StudentMapper">
<!-- 嵌套查询,通过执行一条SQL映射语句来返回预期的特殊类型 -->
<select id="findStudentById" parameterType="Integer"
resultMap="StudentIdCardWithStudentResult">
select * from oto_student where id=#{id}
</select>
<resultMap type="Student" id="StudentIdCardWithStudentResult">
<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.Jorya.mapper.StudentIdCardMapper.findStudentIdCardById"/>
</resultMap>
<!--嵌套查询和嵌套结果查询是两种方法-->
<!--嵌套结果,通过嵌套结果映射来处理重复的联合结果的子集-->
<select id="findStudentById2" parameterType="Integer"
resultMap="StudentIdCardWithStudentResult2">
select s.*,sidcard.code
from oto_student s, oto_studentidcard sidcard
where s.card_id=sidcard.id and s.id=#{id}
</select>
<resultMap type="Student" id="StudentIdCardWithStudentResult2">
<id property="id" column="id" />
<result property="name" column="name" />
<result property="sex" column="sex" />
<association property="studentIdCard" javaType="StudentIdCard">
<id property="id" column="card id" />
<result property="code" column="code" />
</association>
</resultMap>
</mapper>
在上述两个映射文件中使用了MyBatis中的嵌套查询方式进行学生及其关联的学生证信息查询,因为返回的学生对象中除了基本属性外,还有一个关联的studentIdCard属性,所以需要手动编写结果映射。从映射文件StudentMapper.xml中可以看出,嵌套查询的方法是先执行一个简单的SQL语句,然后在进行结果映射时将关联对象在< association>元素中使用select属性执行另一条SQL语句(StudentIdCardMapper.xml中的SQL)。
1.5 测试文件
在com.Jorya.test包中创建测试类MybatisTest,并在类中编写测试方法findStudentByIdTest(),如 1.51所示。
1.51 MybatisAssociatedTest.java
package com.Jorya.test;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import com.Jorya.model.Student;
import com.Jorya.util.MybatisUtil;
public class MybatisTest {
@Test
public void findUserByNameAndJobs() throws Exception{
//通过工具类生产SqlSession对象
SqlSession sqlSession =MybatisUtil.getSession();
//创建User对象,封装需要组合查询的条件
Student student =sqlSession.selectOne("com.Jorya.mapper.StudentMapper.findStudentById",1);
System.out.println(student.toString());
sqlSession.close();
}
@Test
public void findUserByNameAndJobs02() throws Exception{
//通过工具类生产SqlSession对象
SqlSession sqlSession =MybatisUtil.getSession();
//创建User对象,封装需要组合查询的条件
Student student =sqlSession.selectOne("com.Jorya.mapper.StudentMapper.findStudentById2",1);
System.out.println(student.toString());
sqlSession.close();
}
}
在文件1.51的findStudentByIdTest()方法中,首先通过MybatisUtils工具类获取了SqlSession对象,然后通过SqlSession对象的selectOne()方法获取了学生信息,最后关闭了SqlSession。执行方法后,控制台的输出结果如图1.5.1所示。使用MyBatis嵌套查询的方式查询出了学生及其关联的学生证信息,这就是MyBatis中的一对一关联查询。
运行结果图1.5.1
虽然使用嵌套查询的方式比较简单,但是嵌套查询的方式要执行多条SQL语句,这对于大型数据集合和列表展示不是很好,因为这样可能会导致成百上千条关联的SOL语句被执行,从而极大地消耗数据库性能,并且会降低查询效率。为此,MyBatis提供了嵌套结果的方式进行关联查询。在StudentMapper.xml中,使用MyBatis嵌套结果的方式进行学生及其关联的学生证信息查询,所添加的代码如下所示。
<!-- 嵌套结果,通过嵌套结果映射来处理重复的联合结果的子集 -->
<select id="findStudentById2" parameterType="Integer"
resultMap="StudentIdCardWithStudentResult2">
select s.*,sidcard.code
from tb_student s,tb_studentidcard sidcard
where s.card_id=sidcard.id and s.id=#{id}
</select>
<resultMap type="Student" id="StudentIdCardWithStudentResult2">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="sex" column="sex"/>
<!-- 一对一,association 使用select 属性引入另一条SQL语句 -->
<association property="StudentIdCard" javaType="StudentIdCard">
<id property="id" column="card_id"/>
<result property="code" column="code"/>
</association>
</resultMap>
从上述代码中可以看出,MyBatis嵌套结果的方式只编写了一条复杂的多表关联的SQL语句,并且在< association>元素中继续使用相关子元素进行数据库表字段和实体类属性的一一映射。执行结果与图9.1相同,但使用MyBatis嵌套结果的方式只执行了一条SQL语句。
1.6 注意
在使用MyBatis嵌套查询方式进行关联查询映射时,使用MyBatis的延迟加载在一定程度上可以降低运行消耗并提高查询效率。MyBatis默认没有开启延迟加载,需要在核心配置文件mybatis-config.xml中的< settings>元素内进行配置,具体配置方式如下
<settings>
<!--打开延迟加载开关-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--将积极加载改为消极加载,即按需加载-->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
在映射文件中,MyBatis关联映射的< association>元素和< collection>元素中都已默认配置了延迟加载属性,即默认属性fetchType=“lazy”(属性fetchType="eager"表示立即加载),所以在配置文件中开启延迟加载后,无须在映射文件中再做配置
【一对多】
【知识点】
在实际应用中,应用更多的关联关系是一对多(或多对一)。例如,一个班级有多个学生,即多个学生属于一个班级。
MyBatis是怎么处理这种一对多关联关系的呢?在< resultMap>元素中包含一个< collection>子元素,MyBatis就是通过该元素来处理一对多关联关系的。
< collection>子元素的属性大部分与< collection>元素相同,但其还包含一个特殊属性—ofType。ofType属性与javaType属性对应,用于指定实体对象中集合类属性所包含的元素类型。
< collection>元素可以参考如下两种示例进行配置,具体代码如下。
<!--方式一:嵌套查询-->
<collection property="studentList" column="id" ofType="com.Jorya.model.Student"
select ="com.Jorya.mapper.StudentMapper.selectStudent"/>
</collection>
<!--方式二:嵌套结果-->
<collection property="studentList" ofType="com.Jorya.model.Student">
<id property="id" column="student_id"/>
<result property="username" column="username"/>
</collection>
二、【一对多代码示例】(ontToMany)
在了解了MyBatis处理一对多关联关系的元素和方式后,接下来以班级和学生之间的这种一对多关联关系为例详细讲解如何在MyBatis中处理一对多关联关系,具体步骤如下
2.1 创建数据库
在db_mybatis数据库中创建两个数据表:otm_banji和otm_student,同时在表中预先插入几条数据,执行的SQL语句如下所示。
#创建一个名称为otm_banji的表(暂添加少量字段)
CREATE TABLE otm_banji (
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR (32)
);
#插入两条数据
INSERT INTO otm_banji VALUES(1, '自动化151');
INSERT INTO otm_banji VALUES(2, '计算机科学与技术152');
#创建一个名称为otm_student的表(暂时添加少量字段)
CREATE TABLE otm_student(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(32) ,
sex CHAR(1),
banji_id INT ,
FOREIGN KEY (banji_id) REFERENCES otm_banji(id)
);
#插入3条数据
INSERT INTO otm_student VALUES(1,'杨幂','m',1);
INSERT INTO otm_student VALUES(2,'范冰冰','W',1);
INSERT INTO otm_student VALUES(3,'迪丽热巴','m',2);
2.2 配置文件
jar包和MybatisUtils工具类和【一对一代码示例】一样,故不再写
这里写出不同的 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"/>
<settings>
<!-- 打开延迟加载开关 -->
<setting name="lazyLoadingEnabled" value="True"/>
<!--将积极加载改为消息加载,即按需加载 -->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
<typeAliases>
<package name="com.Jorya.model"/>
</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/Jorya/mapper/BanjiMapper.xml"/>
</mappers>
</configuration>
2.3 持久化类
在com.ssm.po包中创建持久化类:班级类Banji和学生类Student,并在两个类中定义相关属性和方法,如 2.3.1和 2.3.2所示。
2.3.1 Banji.java
package com.Jorya.model;
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;
}
public String toString() {
return "Banji [id="+id+",name="+name+",studentList"+studentList+"]";
}
}
2.3.2 Student.java
package com.Jorya.model;
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;
}
public String toString() {
return "StudentIdCard [id="+id+",name=" + name +",sex="+sex+"]";
}
}
2.4 映射文件
在com.ssm.mapper包中创建班级实体映射文件BanjiMapper.xml,并在文件中编写一对多关联映射查询的配置,如 2.41所示。
2.41 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.Jorya.mapper.BanjiMapper">
<!--一对多:查看某一班级及其关联的学生信息
注意:若关联查询出的列名相同,则需要使用别名区分-->
<select id="findBanjiwithStudent" parameterType="Integer"
resultMap="BanjiWithStudentResult">
select b.*,s.id as student_id,s.name
from otm_banji b,otm_student s
where b.id=s.banji_id and b.id=#{id}
<!--
select b.*,s.id as student id,s.name
from otm_banji b,otm_student s
where b.id=s.banji_id and b.id=#{id}
-->
</select>
<resultMap type="Banji" id="BanjiWithStudentResult">
<id property="id" column="id" />
<result property="name" column="name" />
<!--对多关联映射: collection
ofType表示属性集合中元素的类型List<student>属性,即Student类-->
<collection property="studentList" ofType="Student">
<id property="id" column="student_id" />
<result property="name" column="name" />
<result property="sex" column="sex" />
</collection>
</resultMap>
</mapper>
在 2.41中使用MyBatis嵌套结果的方式定义了一个根据班级id查询班级及其关联的学生信息的select语句。因为返回的班级对象中包含Student集合对象属性,所以需要手动编写结果映射信息。
2.5 测试文件
在测试类MyBatisAssociatedTest中编写测试方法findBanjiTest()。
package com.Jorya.test;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import com.Jorya.model.Banji;
import com.Jorya.util.MybatisUtil;
public class MybatisTest {
@Test
public void findBanjiTest() throws Exception{
//通过工具类生产SqlSession对象
SqlSession sqlSession =MybatisUtil.getSession();
//创建User对象,封装需要组合查询的条件
Banji banji =sqlSession.selectOne("com.Jorya.mapper.BanjiMapper.findBanjiwithStudent",1);
System.out.println(banji.toString());
sqlSession.close();
}
}
执行方法后,控制台输出结果如图2.51所示。使用MyBatis嵌套结果的方式查询出了班级及其关联的学生集合信息。这就是MyBatis一对多的关联查询。
图2.51
2.6 注意
上述案例从班级的角度出发,班级与学生之间是一对多的关联关系,但如果从单个学生的角度出发,一个学生只能属于一个班级,即一对一的关联关系。
【多对多】
【知识点】
在实际项目开发中,多对多的关联关系是非常常见的。以学生和课程为例,一个学生可以选修多门课程,而一门课程又可以被多个学生选修,学生和课程就属于多对多的关联关系。在数据库中,多对多的关联关系通常使用一个中间表来维护,中间表选课表(electiveCourse)中的学生id(student_id)作为外键参照学生表的id,课程id(course_id)作为外键参照课程表的id。三个表的关联关系如图3.1 所示。
图3.1
三、【多对多代码示例】(manytomany)
在了解了MyBatis处理一对多关联关系的元素和方式后,接下来以班级和学生之间的这种一对多关联关系为例详细讲解如何在MyBatis中处理一对多关联关系,具体步骤如下
3.1 创建数据库
在db_mybatis数据库中创建两个数据表:mtm_banji和mtm_student,同时在表中预先插入几条数据,执行的SQL语句如下所示。
数据库
#创建一个名称为mtm_course的表
CREATE TABLE mtm_course (
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR (32),
CODE VARCHAR (32)
)
#插入两条数据
INSERT INTO mtm_course VALUES (1, '活着','6666666');
INSERT INTO mtm_course VALUES (2, '冰与火之歌','888888');
#创建一个名称为mtm_electiveCourse 的中间表
CREATE TABLE mtm_electiveCourse (
id INT PRIMARY KEY AUTO_INCREMENT,
student_id INT,
course_id INT,
FOREIGN KEY (student_id) REFERENCES otm_student (id),
FOREIGN KEY (course_id) REFERENCES mtm_course (id)
)
#插入3条数据
INSERT INTO mtm_electiveCourse VALUES (1,1,1) ;
INSERT INTO mtm_electiveCourse VALUES (2,1,2) ;
INSERT INTO mtm_electiveCourse VALUES (3,2,2) ;
3.2持久化类
在com.Jorya.Model包中创建持久化类课程类Course、Student,并在类中定义相关属性和方法,如 3.21和3.22所示。
3.21 Course.java
package com.Jorya.model;
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;
}
public String toString() {
return "Course [id="+id +",name="+name+",code="+code+",StudentList="+studentList+"]";
}
}
3.22 Student.java
package com.Jorya.model;
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;
}
public String toString() {
return "StudentIdCard [id="+id+",name=" + name +",sex="+sex+",CourseList="+courseList+"]";
}
}
3.3 映射文件
在com.Jorya.mapper包中创建课程实体映射文件CourseMapper.xml和学生实体映射文件StudentMapper.xml,对两个映射文件进行编辑后,如3.31和3.32所示。
3.31 CourseMapper.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.Jorya.mapper.CourseMapper">
<!-- 多对多嵌套查询:通过执行一条SQL映射语句来返回预期的特殊类型 -->
<select id="findCourseWithStudent" parameterType="Integer"
resultMap="CourseWithStudentResult">
select * from mtm_course where id=#{id}
</select>
<resultMap type="Course" id="CourseWithStudentResult">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="code" column="code"/>
<collection property="studentList" column="id" ofType="Student"
select="com.Jorya.mapper.StudentMapper.findStudentById">
</collection>
</resultMap>
<!-- 多对多嵌套结果查询:查询某课程极其关联的学生详情 -->
<select id="findCourseWithStudent2" parameterType="Integer"
resultMap="CourseWithStudentResult2">
select c.*,s.id as sid,s.name
from mtm_course c,otm_student s,mtm_electiveCourse ec
where ec.course_id=s.id and c.id=#{id}
</select>
<resultMap type="Course" id="CourseWithStudentResult2">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="code" column="code"/>
<collection property="studentList" ofType="Student">
<id property="id" column="sid"/>
<result property="name" column="name"/>
</collection>
</resultMap>
</mapper>
在3.31中,使用嵌套查询的方式定义了一个id为findCourseWithStudent的select语句来查询课程及其关联的学生信息。在< resultMap>元素中使用了< collection>元素来映射多对多的关联关系,其中property属性表示订单持久化类中的课程属性,ofType属性表示集合中的数据为Student类型,而column的属性值会作为参数执行StudentMapper.xml中定义的id为findStudentById的执行语句来查询订单中的学生信息。
3.32 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.Jorya.mapper.StudentMapper">
<select id="findStudentById" parameterType="Integer" resultType="Student">
select * from otm_student where id in(
select student_id from mtm_electiveCourse where course_id=#{id})
</select>
</mapper>
在3.32中定义了一个id为findStudentById的执行语句,该执行语句中的SQL会根据课程id查询与该课程所关联的学生信息。由于课程和学生是多对多的关联关系,因此需要通过中间表来查询学生信息。
3.4 映射文件
<?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.Jorya.model"/>
</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/Jorya/mapper/CourseMapper.xml"/>
<mapper resource="com/Jorya/mapper/StudentMapper.xml"/>
</mappers>
</configuration>
3.5 测试文件
在测试类MyBatisAssociatedTest中编写多对多关联查询的测试方法findCourseByIdTest(),其代码如下所示。
package com.Jorya.test;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import com.Jorya.model.Course;
import com.Jorya.util.MybatisUtil;
public class MybatisTest {
/**
* 多对多嵌套查询
* @throws Exception
*/
@Test
public void findBanjiTest() throws Exception{
//通过工具类生产SqlSession对象
SqlSession sqlSession =MybatisUtil.getSession();
//创建User对象,封装需要组合查询的条件
Course course=sqlSession.selectOne("com.Jorya.mapper.CourseMapper.findCourseWithStudent",1);
System.out.println(course.toString());
sqlSession.close();
}
@Test
public void findBanjiTest2() throws Exception{
//通过工具类生产SqlSession对象
SqlSession sqlSession =MybatisUtil.getSession();
//创建User对象,封装需要组合查询的条件
Course course=sqlSession.selectOne("com.Jorya.mapper.CourseMapper.findCourseWithStudent2",1);
System.out.println(course.toString());
sqlSession.close();
}
}
执行方法后,控制台的输出结果如图3.5所示。使用MyBatis嵌套查询的方式查询出了课程及其关联的学生信息,这就是MyBatis多对多的关联查询。
图3.5