【图书介绍】《Spring+Spring MVC+MyBatis从零开始学(视频教学版)(第3版)》-CSDN博客
《Spring+Spring MVC+MyBatis从零开始学(视频教学版)(第3版)》(杨章伟,刘祥淼)【摘要 书评 试读】- 京东图书
在现实生活中,一对一关联关系十分常见。例如,一个学生只有一本学生证,同时一本学生证也只对应一个学生。
那么MyBatis是怎么处理这种一对一关联关系的呢?在本书第7章所讲解的<resultMap>元素中包含一个<association>子元素,MyBatis就是通过该元素来处理一对一关联关系的。
在<association>元素中,通常可以配置以下属性。
- property:指定映射到的实体类对象属性,与表字段一一对应。
- column:指定表中对应的字段。
- javaType:指定映射到实体对象属性的类型。
- select:指定引入嵌套查询的子SQL语句,用于关联映射中的嵌套查询。
- fetchType:指定在关联查询时是否启用延迟加载,有lazy和eager两个属性值,默认值为lazy(默认关联映射延迟加载)。
<association>元素有如下两种配置方式。
<!--方式一:嵌套查询-->
<association property="card" column="card_id" javaType="com.ssm.po.StudentIdCard"
select="com.ssm.mapper.StudentIdCardMapper.findCodeById"/>
<!--方式二:嵌套结果-->
<association property="card" javaType="com.ssm.po.StudentIdCard">
<id property="id" column=""card_id"/>
<result property="code" column="code"/>
</association>
注意:MyBatis在映射文件中加载关联关系对象,主要通过两种方式:嵌套查询和嵌套结果。嵌套查询是指通过执行另一条SQL映射语句来返回预期的复杂类型;嵌套结果是使用嵌套结果映射来处理重复的联合结果的子集。
【示例9-1】接下来以学生和学生证之间的一对一关联关系为例进一步进行讲解。
查询学生及其关联的学生证信息,其方法是先通过查询学生表中的主键来获取学生信息,然后通过表中的外键来获取学生证表中的学生证号信息。其具体实现步骤如下。
创建数据表。在db_mybatis数据库中分别创建名为tb_studentidcard和tb_student的数据表,同时预先插入几条数据。其执行的SQL语句如下所示:
#使用数据库db_mybatis
USE db_mybatis;
# 创建一个名称为tb_studentidcard的表
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');
# 创建一个名称为tb_student的表(暂时添加少量字段)
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);
在IntelliJ IDEA中创建一个名为chapter09的Web项目,然后引入相关JAR包、MybatisUtils工具类,以及db.properties、log4j.properties以及mybatis-config.xml核心配置文件。
在项目的com.ssm.po包下创建持久化类:学生证类StudentIdCard和学生类Student,编辑后的代码如文件9.1和文件9.2所示。
文件9.1 StudentIdCard.java
01 package com.ssm.po;
02 //学生证类
03 public class StudentIdCard {
04 private Integer id;
05 private String code;
06 public Integer getId() {
07 return id;
08 }
09 public void setId(Integer id) {
10 this.id = id;
11 }
12 public String getCode() {
13 return code;
14 }
15 public void setCode(String code) {
16 this.code = code;
17 }
18 public String toString() {
19 return "StudentIdCard [id=" + id + ", code=" + code + "]";
20 }
21 }
文件9.2 Student.java
01 package com.ssm.po;
02 //学生类
03 public class Student {
04 private Integer id;
05 private String name;
06 private String sex;
07 private StudentIdCard studentIdCard;
08 public Integer getId() {
09 return id;
10 }
11 public void setId(Integer id) {
12 this.id = id;
13 }
14 public String getName() {
15 return name;
16 }
17 public void setName(String name) {
18 this.name = name;
19 }
20 public String getSex() {
21 return sex;
22 }
23 public void setSex(String sex) {
24 this.sex = sex;
25 }
26 public StudentIdCard getStudentIdCard() {
27 return studentIdCard;
28 }
29 public void setStudentIdCard(StudentIdCard studentIdCard) {
30 this.studentIdCard = studentIdCard;
31 }
32 public String toString() {
33 return "Student [id=" + id + ", name=" + name + ", sex=" + sex +
34 ", studentIdCard=" + studentIdCard + "]";
35 }
36 }
在com.ssm.mapper包中创建学生证映射文件StudentIdCardMapper.xml和学生映射文件StudentMapper.xml,并在两个映射文件中编写一对一关联映射查询的配置信息,如文件9.3和文件9.4所示。
文件9.3 StudentIdCardMapper.xml
01 <?xml version="1.0" encoding="UTF-8"?>
02 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
03 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
04 <mapper namespace="com.ssm.mapper.StudentIdCardMapper">
05 <!--根据id获取学生证信息 -->
06 <select id="findStudentIdCardById" parameterType="Integer" resultType= "StudentIdCard">
07 select * from tb_studentidcard where id=#{id}
08 </select>
09 </mapper>
文件9.4 StudentMapper.xml
01 <?xml version="1.0" encoding="UTF-8"?>
02 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
03 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
04 <mapper namespace="com.ssm.mapper.StudentMapper">
05 <!--嵌套查询,通过执行一条SQL映射语句来返回预期的特殊类型 -->
06 <select id="findStudentById" parameterType="Integer"
07 resultMap="StudentIdCardWithStudentResult">
08 select * from tb_student where id=#{id}
09 </select>
10 <resultMap type="Student" id="StudentIdCardWithStudentResult">
11 <id property="id" column="id"/>
12 <result property="name" column="name"/>
13 <result property="sex" column="sex"/>
14 <!-- 一对一,association使用select属性引入另一条SQL语句 -->
15 <association property="studentIdCard" column="card_id" javaType="StudentIdCard"
16 select="com.ssm.mapper.StudentIdCardMapper.
findStudentIdCardById"/>
17 </resultMap>
18 </mapper>
在上述两个映射文件中,使用了MyBatis中的嵌套查询方式进行学生及其关联的学生证信息查询,因为返回的学生对象中除基本属性外,还有一个关联的studentIdCard属性,所以需要手动编写结果映射。从映射文件StudentMapper.xml中可以看出,嵌套查询的方法是先执行一个简单的SQL语句,然后在进行结果映射时将关联对象在<association>元素中使用select属性引入另一条SQL语句(StudentIdCardMapper.xml中的SQL)。
在核心配置文件mybatis-config.xml中引入Mapper映射文件并定义别名,如文件9.5所示。
文件9.5 mybatis-config.xml
01 <?xml version="1.0" encoding="UTF-8"?>
02 <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
03 "http://mybatis.org/dtd/mybatis-3-config.dtd">
04 <configuration>
05 <!-- 引入数据库连接配置文件 -->
06 <properties resource="db.properties" />
07 <!-- 使用扫描包的形式定义别名 -->
08 <typeAliases>
09 <package name="com.ssm.po"/>
10 </typeAliases>
11 <environments default="mysql">
12 <environment id="mysql">
13 <transactionManager type="JDBC" />
14 <dataSource type="POOLED">
15 <!--数据库驱动 -->
16 <property name="driver" value="${jdbc.driver}" />
17 <!--连接数据库的ur1 -->
18 <property name="url" value="${jdbc.url}" />
19 <!--连接数据库的用户名 -->
20 <property name="username" value="${jdbc.username}" />
21 <!--连接数据库的密码-->
22 <property name="password" value="${jdbc.password}" />
23 </dataSource>
24 </environment>
25 </environments>
26 <!-- 配置Mapper的位置 -->
27 <mappers>
28 <mapper resource="com/ssm/mapper/StudentIdCardMapper.xml" />
29 <mapper resource="com/ssm/mapper/StudentMapper.xml" />
30 </mappers>
31 </configuration>
在上述核心配置文件中,首先引入了数据库连接的配置文件,然后使用扫描包的形式自定义别名,接下来进行环境的配置,最后配置了Mapper映射文件的位置信息。
在com.ssm.test包中创建测试类MybatisAssociatedTest,并在类中编写测试方法findStudentByIdTest(),如文件9.6所示。
文件9.6 MybatisAssociatedTest.java
01 package com.ssm.test;
02 import org.apache.ibatis.session.SqlSession;
03 import org.junit.Test;
04 import com.ssm.po.Student;
05 import com.ssm.util.MybatisUtils;
06 public class MybatisAssociatedTest {
07 /*
08 * 嵌套查询
09 */
10 @Test
11 public void findStudentByIdTest(){
12 SqlSession sqlSession = MybatisUtils.getSession();
13 //使用MyBatis嵌套查询的方法查询id为1的学生信息
14 Student student= sqlSession.selectOne(
15 "com.ssm.mapper.StudentMapper.findStudentById",1);
16 System.out.println(student.toString());
17 sqlSession.close();
18 }
19 }
在文件9.6的findStudentByIdTest()方法中,首先通过MybatisUtils工具类获取SqlSession对象,然后通过SqlSession对象的selectOne()方法获取学生信息,最后关闭SqlSession。执行方法后,控制台的输出结果如图9.1所示。使用MyBatis嵌套查询的方式查询出了学生及其关联的学生证信息,这就是MyBatis中的一对一关联查询。
图9.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 property="studentIdCard" javaType="StudentIdCard">
<id property="id" column="card_id" />
<result property="code" column="code" />
</association>
</resultMap>
从上述代码中可以看出,MyBatis嵌套结果的方式只编写了一条复杂的多表关联的SQL语句,并且在<association>元素中继续使用相关子元素进行数据库表字段和实体类属性的一一映射。执行结果与图9.1所示的结果相同,但使用MyBatis嵌套结果的方式只执行了一条SQL语句。
注意:在使用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"表示立即加载),所以在配置文件中开启延迟加载后,无须在映射文件中再进行配置。