复杂查询的环境搭建:为接下来的多对一和一对多的处理做准备。
总体步骤为:
pom.xml
中导入所需要的依赖;mybatis-config.xml
;MybatisUtils.java
;Teacher.java
和 Student.java
;TeacherMapper.java
和 StudentMapper.java
;TeacherMapper.xml
和 StudentMapper.xml
;需要提前建立两张表:student
和 teacher
表。
-- 创建数据库 mybatis01
CREATE DATABASE IF NOT EXISTS `mybatis01`;
USE `mybatis01`;
-- 创建 teacher 表
CREATE TABLE IF NOT EXISTS `teacher` (
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4;
-- 插入数据
INSERT INTO `teacher` (`id`, `name`) VALUES (1, '张老师'), (2, '王老师');
-- 创建 student 表
CREATE TABLE IF NOT EXISTS `student` (
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
`tid` INT(10) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `fktid` (`tid`)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4;
-- 插入数据
INSERT INTO `student` (`id`, `name`, `tid`) VALUES
(1, '小明', 1),
(2, '小红', 1),
(3, '小张', 2),
(4, '小李', 2),
(5, '小王', 2);
环境搭建显示
注意:这里把实现类
Mapper.xml
放入了资源文件夹下,但经过编译后仍在dao
包下,所以在核心配置文件中的资源路径为:resource="com/Sun3285/dao/StudentMapper.xml"
。
生成的 target
文件夹
最佳实践:最好逐步建立结果映射。单元测试可以在这个过程中起到很大帮助。 如果你尝试一次性创建复杂的结果映射,不仅容易出错,难度也会直线上升。 所以,从最简单的形态开始,逐步迭代。而且别忘了单元测试! 有时候,框架的行为像是一个黑盒子(无论是否开源)。因此,为了确保实现的行为与你的期望相一致,最好编写单元测试。 并且单元测试在提交 bug 时也能起到很大的作用。
举例:查询全部学生(多)对应的老师(少)。
思路:
- 联表查询是按照结果嵌套处理;
- 步骤:先写出 SQL 语句,保证可以执行,然后在
Mapper.xml
中用resultMap
进行结果集映射,最后测试。
SELECT s.`id` AS '学生id', s.`name` AS '学生姓名', s.`tid` AS '老师id', t.`name` AS '老师姓名'
FROM `student` AS s
INNER JOIN `teacher` AS t
ON s.`tid` = t.`id`;
Mapper.xml
中用 resultMap
进行结果集映射:<select id="getAllStudents" resultMap="studentResultMap">
SELECT s.`id` AS '学生id', s.`name` AS '学生姓名', s.`tid` AS '老师id', t.`name` AS '老师姓名'
FROM `student` AS s
INNER JOIN `teacher` AS t
ON s.`tid` = t.`id`;
select>
<resultMap id="studentResultMap" type="Student">
<result property="id" column="学生id"/>
<result property="name" column="学生姓名"/>
<association property="teacher" javaType="Teacher">
<result property="id" column="老师id"/>
<result property="name" column="老师姓名"/>
association>
resultMap>
思路:子查询是按照查询过程嵌套处理;
<select id="getAllStudents" resultMap="studentResultMap">
select * from `student`;
select>
<resultMap id="studentResultMap" type="Student">
<result property="id" column="id"/>
<result property="name" column="name"/>
<association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
resultMap>
<select id="getTeacher" resultType="Teacher">
select * from teacher where `id` = #{tid};
select>
举例:查询全部老师(少)包含的学生(多)。
思路:
- 联表查询是按照结果嵌套处理;
- 步骤:先写出 SQL 语句,保证可以执行,然后在
Mapper.xml
中用resultMap
进行结果集映射,最后测试。
SELECT t.`id` AS '老师id', t.`name` AS '老师姓名', s.`id` AS '学生id', s.`name` AS '学生姓名'
FROM `teacher` AS t
INNER JOIN `student` AS s
ON t.`id` = s.`tid`;
Mapper.xml
中用 resultMap
进行结果集映射:<select id="getAllTeachers" resultMap="teacherResultMap">
SELECT t.`id` AS '老师id', t.`name` AS '老师姓名', s.`id` AS '学生id', s.`name` AS '学生姓名'
FROM `teacher` AS t
INNER JOIN `student` AS s
ON t.`id` = s.`tid`;
select>
<resultMap id="teacherResultMap" type="Teacher">
<result property="id" column="老师id"/>
<result property="name" column="老师姓名"/>
<collection property="studentList" ofType="Student">
<result property="id" column="学生id"/>
<result property="name" column="学生姓名"/>
collection>
resultMap>
This part is too complex, let’s use linked table queries instead!
当查询出来的字段名与属性名不一致时,需要进行结果集映射 resultMap
。
使用结果集映射 resultMap
时,属性名和查询到的字段名要一一映射:
要保证 SQL 的可读性,通俗易懂。
排查错误可以使用日志。
动态 SQL 的环境搭建:为接下来的动态 SQL 学习做准备。
总体步骤为:
pom.xml
中导入所需要的依赖;mybatis-config.xml
;MybatisUtils.java
以及 IDUtils.java
(用来产生一个唯一的 ID);Blog.java
;BlogMapper.java
;BlogMapper.xml
;需要提前建立一张表:blog
表。
-- 创建数据库 mybatis02
CREATE DATABASE IF NOT EXISTS `mybatis02`;
USE `mybatis02`;
-- 创建表 blog
CREATE TABLE IF NOT EXISTS `blog`(
`id` VARCHAR(50) NOT NULL COMMENT '博客id',
`title` VARCHAR(100) NOT NULL COMMENT '博客标题',
`author` VARCHAR(30) NOT NULL COMMENT '博客作者',
`create_time` DATETIME NOT NULL COMMENT '创建时间',
`views` INT(30) NOT NULL COMMENT '浏览量'
)ENGINE=INNODB DEFAULT CHARSET=utf8mb4;
测试:插入数据。
环境搭建过程
动态 SQL:根据不同的条件生成不同的 SQL 语句,在 SQL 层面执行一些逻辑代码。
实质:拼接 SQL 语句,只要保证 SQL 的正确性,按照 SQL 的格式去排列组合就可以。
建议:先写出完整的 SQL,然后再对应地去修改成为动态 SQL。
<select id="方法名" parameterType="参数类型" resultType="返回值类型">
select 查询语句
<where>
<if test="判断语句1(结果为布尔值类型)">
sql 语句
if>
<if test="判断语句2(结果为布尔值类型)">
sql 语句
if>
where>
select>
如果有多个 IF
语句,会从上到下依次对每个条件进行判断。有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose
元素,它有点像 Java 中的 switch 语句。
<select id="方法名" parameterType="参数类型" resultType="返回值类型">
select 查询语句
<where>
<choose>
<when test="判断语句1(结果为布尔值类型)">
sql 语句
when>
<when test="判断语句2(结果为布尔值类型)">
sql 语句
when>
<otherwise>
sql 语句
otherwise>
choose>
where>
select>
注意:
choose
元素,类似于 Java 中的 switch 语句:
- choose 等同于 switch;
- when 等同于 case;
- otherwise 等同于 default。
where
元素作用:
set
元素作用:
foreach:用来对集合进行遍历(尤其是在构建 IN 条件语句的时候)。
可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。
当使用可迭代对象或者数组时:
collection
:指传递的集合参数,如果 parameterType
为 Map 集合,这里就是指键名;
item
:每次迭代获取到的元素;
open
:以 xxx 为开始;
separator
:分隔;
close
:以 xxx 为结束。
<select id="方法名" parameterType="map" resultType="返回值类型">
select 查询语句
<where>
<foreach collection="传递的集合参数" item="每次迭代获取到的元素"
open="开始" separator="分隔" close="结束">
`id` = #{id}
foreach>
where>
select>
举例:查询指定 ID 的博客信息。
有的时候,我们可能会将一些公共部分抽取出来,方便复用,如下所示:
<sql id="SQL 片段的 id(自由取)">
公共的 SQL 片段
sql>
引用 SQL 片段时,如下所示:
<select id="方法名" parameterType="map" resultType="返回值类型">
select 查询语句
<where>
<include refid="SQL 片段的 id">include>
where>
select>
注意:
- 最好基于单表来定义 SQL 片段;
- SQL 片段中不要存在 where 标签。
多个 IF
语句会从上到下依次对每个条件进行判断;
choose
元素,像 Java 中的 switch 语句,从上到下执行,只要有一个符合就停止执行;
foreach
元素是遍历循环,集合参数为可迭代对象(如 List、Set 等)、Map 对象或者数组对象;
最好将 SQL 中的 where 用 where
标签代替;
判断 test
时,里面的语句为 Java 语句,其中对字符串的判断,要用单引号套双引号的形式,因为 MyBatis 是使用 OGNL 表达式来进行解析的。
<where>
<if test='author == "Sun3285"'>
SQL 语句
if>
where>
缓存:Cache,存在内存中的临时数据。
MyBatis 缓存:包含了一个非常强大的缓存特性,可以非常方便地定制和配置缓存。默认定义了两级缓存:
sqlSession
级别的缓存。默认情况下,只有一级缓存开启,仅仅对一个会话中的数据进行缓存。namespace
级别的缓存。需要手动开启和配置,MyBatis 定义了缓存接口 Cache
,可以实现接口来定义二级缓存。注意:二级缓存只作用于 cache 标签所在的映射文件中的语句。
一级缓存是 sqlSession
级别的缓存,默认情况下,只有一级缓存开启;
仅仅对一个会话中的数据进行缓存(sqlSession
从得到到关闭这一段期间的数据)。
测试一级缓存。
缓存失效的情况:
Mapper.xml
;sqlSession.clearCache();
。注意:一级缓存相当于一个 Map 集合。
二级缓存是 namespace
级别的缓存,会在整个 Mapper.xml
中生效。需要手动开启和配置。
工作机制:当关闭会话时,一级缓存中的数据才会被保存到二级缓存中,新的会话查询信息时,就可以从二级缓存中获取内容。
开启二级缓存步骤:
Mapper.xml
中加 cache
标签。测试二级缓存。
注意:实体类需要序列化。
在缓存中查询的顺序:
举例:
查询一个数据 --> 二级缓存中没有 --> 一级缓存中没有 --> 连接数据库,查询数据库 --> 查询到结果,结果保存到一级缓存中 --> 关闭会话,此时一级缓存中的数据保存到二级缓存中;
再次查询同一个数据 --> 二级缓存中存在 --> 从二级缓存中直接拿到数据(不会连接数据库)。
target
文件中的路径和全限名。resultType
和 resultMap
别写错。Serializable
接口。