這是基于b站狂神的JavaWeb課程的筆記 課程鏈接
筆記第一部分鏈接:MyBatis 入門筆記+理解(一)
9. 復(fù)雜查詢之多對一
9.1 環(huán)境搭建
- sql建表
CREATE TABLE `teacher` (
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
INSERT INTO teacher(`id`, `name`) VALUES (1, 秦老師);
CREATE TABLE `student` (
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
`tid` INT(10) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `fktid` (`tid`),
CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
INSERT INTO `student` (`id`, `name`, `tid`) VALUES (1, 小明, 1);
INSERT INTO `student` (`id`, `name`, `tid`) VALUES (2, 小紅, 1);
INSERT INTO `student` (`id`, `name`, `tid`) VALUES (3, 小張, 1);
INSERT INTO `student` (`id`, `name`, `tid`) VALUES (4, 小李, 1);
INSERT INTO `student` (`id`, `name`, `tid`) VALUES (5, 小王, 1);
- 構(gòu)造實體類
學(xué)生
@Data
public class Student {
private int id;
private String name;
// 學(xué)生需要關(guān)聯(lián)一個老師
private Teacher teacher;
}
老師
@Data
public class Teacher {
private int id;
private String name;
}
9.2 多對一
- 現(xiàn)有需求:獲取所有學(xué)生的信息和他們關(guān)聯(lián)的老師的信息
多名學(xué)生和一個老師關(guān)聯(lián)
需求對應(yīng)的sql語句:
select s.id,s.name,t.id,t.name
from student s, teacher t
where s.tid = t.id;
- 思路:
- 查詢出所有學(xué)生的信息
- 根據(jù)查出來學(xué)生的tid,查詢出對應(yīng)關(guān)聯(lián)的老師的信息
- 根據(jù)思路編寫Mapper.xml:
方法一:根據(jù)查詢嵌套處理
<!-- 根據(jù)查詢嵌套處理 -->
<!-- 1. 查詢所有學(xué)生信息 -->
<select id="getStudents" resultMap="studentAndTeacher">
select * from student
</select>
<!-- 兩次查詢用一個resultMap關(guān)聯(lián)起來 -->
<resultMap id="studentAndTeacher" type="student">
<id property="id" column="id"/>
<result property="name" column="name"/>
<!--
復(fù)雜的屬性,單獨處理:對象用association垮抗,集合用collection
屬性是一個對象陆盘,所以先用javaType來規(guī)定是哪個類
而從表中獲得的是一個tid,所以需要用一個字查詢語句恤磷,用tid獲取整個Teacher對象
select="getTeachers"就規(guī)定了用如下的這個sql去獲得teacher的信息
-->
<association property="teacher" column="tid" javaType="Teacher" select="getTeachers"/>
</resultMap>
<!-- 2. 根據(jù)學(xué)生查出來的tid面哼,查詢對應(yīng)的老師! (子查詢) -->
<select id="getTeachers" resultType="teacher">
select * from teacher where id=#{tid}
</select>
- 本質(zhì)上碗殷,第一個
<select>
中select * from student
得到的表字段為tid
而學(xué)生類的屬性為teacher
精绎,所以需要一個結(jié)果集映射將他們對應(yīng)起來 - 而恰好
teacher
是一個對象 (用javaType="Teacher"
表示),不能簡單地只將名稱進行映射锌妻,還需要得到teacher
的內(nèi)部屬性信息代乃,所以我們又需要一個子查詢,就是第二個<select>
中的sql查詢仿粹。
方法二:根據(jù)結(jié)果嵌套處理
<!-- 根據(jù)結(jié)果嵌套處理 -->
<select id="getStudents2" resultMap="mapTeacher">
select s.id sid, s.name sname, t.id tid, t.name tname
from student s, teacher t
where s.tid = t.id
</select>
<resultMap id="mapTeacher" type="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<association property="teacher" javaType="Teacher">
<!--
根據(jù)查詢到的結(jié)果(tid, tname),
來與Teacher這個類中的屬性進行映射
-->
<result property="id" column="tid"/>
<result property="name" column="tname"/>
</association>
</resultMap>
10. 復(fù)雜查詢之一對多
與9中的情況相對應(yīng)搁吓,一個老師擁有多個學(xué)生,此時稱為一對多
11.1 環(huán)境搭建
- 表與9中相同
- 實體類
老師
@Data
public class Teacher {
private int id;
private String name;
// 一個老師擁有多個學(xué)生
private List<Student> students;
}
學(xué)生
@Data
public class Student {
private int id;
private String name;
private int tid;
}
10.2 一對多處理
- 需求:根據(jù)id查詢老師信息和這個老師擁有的所有學(xué)生信息
- sql:
select t.id tid, t.name tname, s.id sid, s.name sname
from student s, teacher t
where t.id = s.tid and t.id = #{tid}
- 接口:
public interface TeacherMapper {
// 根據(jù)id查詢老師信息和這個老師擁有的所有學(xué)生信息
// 使用@param("tid")后吭历,則Mapper.xml中的sql語句中應(yīng)用對應(yīng)的#{tid}
// 提取該參數(shù)
Teacher getTeacher(@Param("tid") int id);
}
方法一:根據(jù)結(jié)果嵌套處理
<!-- 根據(jù)結(jié)果嵌套處理 -->
<select id="getTeacher" resultMap="tsMap">
select t.id tid, t.name tname, s.id sid, s.name sname
from student s, teacher t
where t.id = s.tid and t.id = #{tid}
</select>
<resultMap id="tsMap" type="teacher">
<!-- 名稱映射 -->
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<!--
屬性為集合時 ==> collection
集合中的泛型用 ofType 規(guī)定
-->
<collection property="students" ofType="student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
</collection>
</resultMap>
- 先拿到結(jié)果
t.id tid, t.name tname, s.id sid, s.name sname
- 再用拿到的這些結(jié)果與老師的屬性堕仔,以及老師擁有的學(xué)生的屬性作一一映射
方法二:根據(jù)查詢嵌套處理
<!-- 根據(jù)查詢嵌套處理 -->
<!-- 先根據(jù)id拿到老師 -->
<select id="getTeacher2" resultMap="tsMap2">
select * from teacher
where id = #{tid}
</select>
<resultMap id="tsMap2" type="teacher">
<!--
注意:這里即使名稱相同,也要顯式地映射一次
因為column中的id在下面被映射給了"students"
這里不映射會導(dǎo)致查不到teacher的id
-->
<result property="id" column="id"/>
<!--
屬性名稱為students
類為Arraylist晌区,集合中的泛型為student
再用一個子查詢?nèi)ゲ閠id為"id"的學(xué)生
-->
<collection property="students" javaType="Arraylist" ofType="student" column="id" select="getStudent"/>
</resultMap>
<!-- 再根據(jù)老師的id拿到tid為老師的id的學(xué)生 -->
<select id="getStudent" resultType="student">
select * from student
where tid = #{id}
</select>
11. 動態(tài)SQL
- 減少了根據(jù)不同條件拼接 SQL 語句的痛苦
11.1 If
一個官方文檔中的例子足矣:
<select id="findActiveBlogWithTitleLike"
resultType="Blog">
SELECT * FROM BLOG
WHERE state = ‘ACTIVE’
<if test="title != null">
AND title like #{title}
</if>
</select>
一般將一個 Map
作為參數(shù)摩骨,若在 Map
中放入了 title
的值,則可以被取出并放到 <if>
中的 AND
語句里
11.2 Choose
與Java中的 switch
類似:
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<choose>
<when test="title != null">
AND title like #{title}
</when>
<when test="author != null and author.name != null">
AND author_name like #{author.name}
</when>
<otherwise>
AND featured = 1
</otherwise>
</choose>
</select>
根據(jù) <when>
的編寫順序查看是否滿足條件朗若,只會執(zhí)行第一條滿足條件的 <when>
恼五,若沒有滿足條件的則執(zhí)行 <otherwise>
11.3 Where, Set, Trim
Where
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG
<where>
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</where>
</select>
官方doc一句話:
where 元素只會在子元素返回任何內(nèi)容的情況下才插入 “WHERE” 子句。而且哭懈,若子句的開頭為 “AND” 或 “OR”灾馒,where 元素也會將它們?nèi)コ?/p>
也就是說在上面這個例子中,若沒有一個 if
被滿足遣总,where將自動消失睬罗,若只有第二個 if
或 只有第三個 if
被滿足,他們前面的 AND
將自動消失旭斥。
Set
<update id="updateAuthorIfNecessary">
update Author
<set>
<if test="username != null">username=#{username},</if>
<if test="password != null">password=#{password},</if>
<if test="email != null">email=#{email},</if>
<if test="bio != null">bio=#{bio}</if>
</set>
where id=#{id}
</update>
這個例子中容达,set 元素會動態(tài)地在行首插入 SET 關(guān)鍵字,并會刪掉額外的逗號
trim
可自定義 where
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim>
也可自定義 set
<trim prefix="SET" suffixOverrides=",">
...
</trim>
11.4 foreach
需求的SQL:
SELECT * FROM user
WHERE gender = 'male'
AND ( id = 1 OR id = 2 OR id = 4 )
- 在接口的參數(shù)中使用
Map<String, Object>() map
- 在map中添加一個list
ArrayList<Integer> ids = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
map.put("ids", ids)
在mapper.xml中編寫
<select id="selectPostIn" resultType="domain.blog.Post">
SELECT *
FROM user
WHERE Gender = #{male}
<foreach item="id" collection="ids"
open=" AND (" separator="OR" close=")">
#{id}
</foreach>
</select>
12. 緩存
12.1 一級緩存
- 默認開啟
-
Sqlsession
級別的緩存 - 通俗地說垂券,一級緩存只存在于其對應(yīng)的一個
Sqlsession
的生命周期中:
// 對于這個sqlSession的緩存董饰,只存在于這兩行代碼之間
SqlSession sqlSession = MyBatisUtils.getSqlSession();
...
sqlSession.close()
- 在一個
sqlSession
中,查詢同一個東西,只會在第一次查詢那個東西時連接到數(shù)據(jù)庫卒暂,之后都會直接從緩存中拿去 - 有任何增啄栓、刪、改的操作都會清除緩存
-
sqlSession.clearCache()
也可手動清除緩存 - 一級緩存就是一個Map
12.2 二級緩存
- 基于
namespace
級別的緩存 - 需手動開啟
<!-- 顯式地開啟全局緩存> <setting name="cacheEnabled" value="true"/>
也可以自定義參數(shù)<!-- 在當(dāng)前Mapper.xml中使用二級緩存 --> <cache/>
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
- 開啟后也祠,當(dāng)一個會話結(jié)束了昙楚,其對應(yīng)的緩存在消失之前會進入到二級緩存,也就是當(dāng)前Mapper的緩存中
12.3 查看緩存的順序
在一次會話中做一次查詢
- 先查看當(dāng)前Mapper中的二級緩存中有沒有查詢結(jié)果诈嘿,若沒有
- 再查看當(dāng)前會話對應(yīng)的一級緩存中有沒有堪旧,若沒有
- 查詢數(shù)據(jù)庫