文章目錄
MyBatis學習
博主此篇學習筆記是跟隨嗶哩嗶哩UP主:狂神說Java,視頻講解時整理
UP主鏈接:https://space.bilibili.com/95256449
簡介:
MyBatis是一款優秀的持久層框架
它支持定製化SQL、存儲過程以及高級映射。
MyBatis避免了幾乎所有的JDBC代碼和手動設置參數以及獲取結果集。
MyBatis可以使用簡單的XML或註解來配置和映射原生類型、接口和Java的POJO爲數據庫中的記錄。
特點:
- 簡單易學、靈活、sql和代碼的分離,提高了可維護性。
- 提供映射標籤,支持對象與數據庫的orm字段關係映射;
- 提供對象關係映射標籤,支持對象關係組建維護
- 提供xml標籤,支持編寫動態sql。
使用:
1、搭建環境(如:pom.xml中加入mysql驅動、mybatis、junit一些依賴)
<!-- 導入依賴-->
<dependencies>
<!-- mysql驅動-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.19</version>
</dependency>
<!-- mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<!-- junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
</dependency>
</dependencies>
2、創建一個模塊(加入mybatis核心配置文件:mybatis-config.xml(resources文件中創建),mybatis工具類)
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>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT%2B8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!-- 每一個Mapper.XML都需要在Mybatis核心配置文件中註冊-->
<mappers>
<mapper resource="Mapper/UserMapper.xml"/>
</mappers>
</configuration>
MybatisUtils.java
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
//使用Mybatis第一步:獲取sqlSessionFactory對象;
String resource = "mybatis-config.xml";
InputStream inputStream = null;
inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//即然有了SqlSessionFactory,顧名思義,我們就可以從中獲得SqlSession的實例。
//SqlSession完全包含了面向數據庫執行SQL命令所需的所有方法。
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
3、編寫代碼(創建pojo包中實體類,mapper接口(相當於dao接口),mapper.xml文件(寫sql語句,相當於dao接口的實現類;在resources文件中創建))
User.java
public class User {
private int id;
private String name;
private String pwd;
public User() {
}
public User(int id, String name, String pwd) {
this.id = id;
this.name = name;
this.pwd = pwd;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
}
UserMapper.java
package com.kx.dao;
import com.kx.pojo.User;
import java.util.List;
public interface UserMapper {
//查詢所有
List<User> getUserList();
//根據id查詢
User getUser(int id);
//增加一條數據
int insertUser(User user);
//修改數據
int updateUser(User user);
//刪除數據
int deleteUser(int id);
}
UserMapper.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.kx.dao.UserMapper">
<!-- 查詢所有-->
<select id="getUserList" resultType="com.kx.pojo.User">
select * from mybatis.user
</select>
<!-- 根據id查詢-->
<select id="getUser" resultType="com.kx.pojo.User">
select * from user where id = #{id}
</select>
<!-- 插入數據-->
<insert id="insertUser" parameterType="com.kx.pojo.User">
insert into user (id,name,pwd) value (#{id},#{name},#{pwd})
</insert>
<!-- 修改數據-->
<update id="updateUser" parameterType="com.kx.pojo.User">
update user set name=#{name},pwd=#{pwd} where id = #{id}
</update>
<!-- 刪除數據-->
<delete id="deleteUser" parameterType="int">
delete from user where id = #{id}
</delete>
</mapper>
4、測試(創建測試類進行創建)
UserDaoTest.java
import com.kx.pojo.User;
import com.kx.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
public class UserDaoTest {
@Test
public void test(){
//第一步:獲得SqlSession對象
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = userMapper.getUserList();
for (User user : userList) {
System.out.println(user);
}
//關閉SqlSession
sqlSession.close();
}
@Test
public void getUser(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.getUser(1);
System.out.println(user);
sqlSession.close();
}
@Test
public void insertUser(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int res = mapper.insertUser(new User(3, "哈哈哈", "46567"));
if (res>0){
System.out.println("插入成功");
}
sqlSession.commit();
sqlSession.close();
}
@Test
public void updateUser(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int res = mapper.updateUser(new User(3, "嘿嘿嘿", "75644"));
sqlSession.commit();
sqlSession.close();
}
@Test
public void deleteUser(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int res = mapper.deleteUser(3);
sqlSession.commit();
sqlSession.close();
}
}
增刪改查
namespace
namespace的包名要和mapper接口的包名一致
select、insert、update、Delete
選擇,查詢語句;
id:就是對應的namespace中的方法名;
resultType:Sql語句執行的返回值
parameterType:參數類型
步驟
1、編寫接口
2、編寫對應的mapper中的sql語句
3、測試
注意:增刪改需要提交事務
配置解析
1、核心配置文件
mybatis-config.xml
MyBatis的配置文件包含了會深深影響MyBatis行爲的設置和屬性信息
configuration(配置)
properties(屬性)
setting(設置)
typeAliases(類型別名)
typeHandlers(類型處理器)
objectFactory(對象工廠)
plugins(插件)
environments(環境配置)
environment(環境變量)
transactionManager(事務管理器)
dataSource(數據源)
databaseIdProvider(數據庫廠商標識)
mappers(映射器)
2、環境配置(environments)
MyBatis可以配置成適應多種環境
注意:儘管可以配置多個環境,但每個SqlSessionFactory實例只能選擇一種環境。
Mybatis默認的事務管理器就是JDBC,連接池:POOLED
3、屬性(properties)
可以通過properties屬性來實現引用配置文件
這些屬性都是可外部配置且可動態替代的,既可以在典型的java屬性文件中配置,也可通過properties元素的子元素來傳遞。
編寫配置文件
db.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT%2B8
username=root
password=123456
在覈心配置文件中映入
<!-- 引入外部配置文件-->
<properties resource="db.properties">
<property name="username" value"root"/>
<property name="pwd" value="123456"/>
</properties>
- 可以直接引入外部文件
- 可以在其中增加一些屬性配置
- 如果兩個文件有統一字段,優先使用外部配置文件
4、類型別名(typeAliases)
- 類型別名是爲了Java類型設置一個短的名字。
- 存在的意義僅在於用來減少類完全限定名的冗餘。
<!-- 可以給實體類起別名-->
<typeAliases>
<typeAlias type="com.kx.pojo.User" alias="User"/>
</typeAliases>
也可以指定一個包名,MyBatis會在包名下面搜索需要的Java Bean,比如:掃描實體類的包,它的默認別名就爲這個類的類名,首字母小寫
<typeAliases>
<package name="com.kx.pojo"/>
</typeAliases>
在實體類比較少使,使用第一種方式
若實體類十分多,鍵入使用第二種
第一種可以DIY別名,第二種則需要在實體上增加註解纔可DIY別名
@Alias("user")
public class User{}
5、映射器(mappers)
MapperRegistry:註冊綁定我們的Mapper文件;
方式一:
<!-- 每一個Mapper.XML都需要在Mybatis核心配置文件中註冊-->
<mappers>
<mapper resource="Mapper/UserMapper.xml"/>
</mappers>
方式二:使用class文件綁定註冊
<mappers>
<mapper class="com.kx.daoUserMapper"/>
</mappers>
方式三:使用掃描包進行注入綁定
<mappers>
<package name="com.kx.dao"/>
</mappers>
注意:
方式二和方式三中
- 接口和它的Mapper配置文件必須同名
- 接口和它的Mapper配置文件必須在同一個包下
6、生命週期和作用域
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-l6eukVGs-1586162967745)(C:\Users\K.X\AppData\Roaming\Typora\typora-user-images\image-20200331155439999.png)]
生命週期,和作用域,是至關重要的,因爲錯誤的使用會導致非常嚴重的併發問題
SqlSessionFactoryBuilder:
- 一旦創建了SqlSessionFactory,就不再需要它了
- 局部變量
SqlSessionFactory:
- 可以想象爲:數據庫連接池
- SqlSessionFactory一旦被創建就應該在應用的運行期限一直存在,沒有任何理由丟棄它或重新創建另一實例。
- 因此SqlSessionFactory的最佳作用域是應用作用域。
- 最簡單的就是使用單例模式或者靜態單例模式。
SqlSession
- 連接到連接池的一個請求!
- SqlSession的實例不是線程安全的,因此是不能被共享的,所以它的最佳的作用域是請求或方法作用域。
- 用完之後需要關閉,否則資源被佔用
解決屬性名和字段名不一致的問題
實體類中的屬性名和數據庫的屬性名不一致:
解決方法
方式一:起別名
<select id="getUsers" resultType="user"> select id,name,pwd as password from user where id=#{id} </select>
方式二
resultMap
<!--結果集映射--> <resultMap id="UserMap" type="user"> <!--column數據庫中的字段,property實體類中的屬性--> <result column="id" property="id"/> <result column="name" property="name"/> <result column="pwd" property="password"/> </resultMap> <select id="getUserById" resultMap="UserMap"> select * from user where id = #{id} </select>
- ResultMap的設計思想是,對於簡單的語句根本不需要配置顯式的結果映射,而對於複雜一點的語句只需要描述它們的關係就行了。對於沒有變化的字段可以不用映射,只對字段不同的映射就好了
<resultMap id="UserMap" type="user"> <!--column數據庫中的字段,property實體類中的屬性--> <result column="pwd" property="password"/> </resultMap>
日誌
日誌工廠
如果一個數據庫操作,出現了異常,日誌就是最好的助手!
logImpl 指定MyBatis所用日誌的具體實現,未指定時將自動查找
- SLF4J
- LOG4J
- LOG4J2
- JDK_LOGGING
- COMMONS_LOGGING
- STDOUT_LOGGING
- NO_LOGGING
在Mybatis中具體使用哪一個日誌實現,在設置中設定
STDOUT_LOGGING標準日誌輸出
在mybatis核心配置文件中,配置日誌
<!--標準工廠日誌實現-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
Log4j
- Log4j是Apache的一個開源項目,通過使用Log4j,我們可以控制日誌信息輸送的目的地是控制檯、文件、GUI組件
- 也可以控制每一條日誌的輸出格式;
- 通過定義每一條日誌信息的級別,我們能夠更加細緻地控制日誌的生成過程
- 通過一個配置文件來靈活地進行配置,而不需要修改應用的代碼
1、先導入log4j的包
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
2、log4j.properties
#將等級爲DEBUG的日誌信息輸出到console和file這兩個目的地,console和file的定義在下面的代碼
log4j.rootLogger=DEBUG,console,file
#控制檯輸出的相關設置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n
#文件輸出的相關設置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/kx.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n
#日誌輸出級別
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
3、配置log4j爲日誌的實現
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
簡單使用
1.在要使用Log4j的類中,導入包import org.apache.log4j.Logger;
2.日誌對象,參數爲當前類的class
static Logger logger = Logger.getLogger(UserDaoTest.class);
3.日誌級別
logger.info("info:進入了testLog4j");
logger.debug("debug:進入了testLog4j");
logger.error("error:進入了testLog4j");
分頁
完成返回前幾條或者中間某幾行數據,減少數據的處理量
使用Limit分頁
語法:
select * from user limit startIndex,pageSize;
select * from user limit n; #[0,n]
接口
//分頁查詢
List<User> getUserByLimit(Map<String,Integer> map);
Mapper.xml
<!-- 分頁查詢-->
<select id="getUserByLimit" parameterType="map" resultType="User">
select * from user limit #{startIndex},#{pageSize}
</select>
測試
@Test
public void getUserByLimit(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map<String,Integer> map = new HashMap<String, Integer>();
map.put("startIndex",0);
map.put("pageSize",1);
List<User> userList = mapper.getUserByLimit(map);
for (User user : userList) {
System.out.println(user);
}
//關閉SqlSession
sqlSession.close();
}
使用註解開發
面向接口編程
根本原因:解耦,可拓展,提高複用,分層開發中,上層不用管具體的實現,大家都遵守共同的標準,使得開發變得容易,規範性更好
-在一個面向對象的系統中,系統的各種功能是由許許多多的不同對象協作完成的。在這種情況下,各個對象內部是如何實現自己的,對系統設計人員來說就不那麼重要了;
-而各個對象之間的協作關係則成爲系統設計的關鍵。小到不同類之間的通信,大到各模塊之間的交互,在系統設計之初都是要着重考慮的,這也是系統設計的主要工作內容。面向接口編程就是指按照這種思想來編程。
關於接口的理解
-接口從更深層次的理解,應是定義(規範,約束)與實現(名實分離的原則)的分離。
-接口的本身反映了系統設計人員對系統的抽象理解。
-接口應有兩類:
-一類是對一個個體的抽象,它可對應爲一個抽象體(abstract class);
-一類是對一個個體某一方面的抽象,即形成一個抽象面(interface);
-一個個體有可能有多個抽象面。抽象體與抽象面是有區別的。
三個面向區別
-面向對象是指,我們考慮問題時,以對象爲單位,考慮它的屬性及方法。
-面向過程是指,我們考慮問題時,以一個具體的流程(事務過程)爲單位,考慮它的實現。
-接口設計與非接口設計是針對複用技術而言的,與面向對象(過程)不是一個問題。更多的體現就是對系統整體的架構
使用註解開發
-
註解在接口上實現
@Select("select * from user") List<User> getUserList();
-
需要在覈心配置文件中綁定接口
<mappers> <mapper class="com.kx.dao.UserMapper"/> </mappers>
CRUD
我們可以在工具類創建時實現自動提交事務
public static SqlSession getSqlSession() {
return sqlSessionFactory.openSession(true);
}
編寫接口,增加註解
public interface UserMapper {
@Select("select * from user")
List<User> getUserList();
//方法存在多個參數,所有參數前必須加上 @Param("id")註解
@Select("select * from user where id = #{id}")
User getByID(@Param("id") int id);
@Insert("insert into user(id,name,pwd) values (#{id},#{name},#{pwd})")
int insertUser(User user);
@Update("update from user set name=#{name},pwd=#{pwd} where id=#{id}")
int updateUser(User user);
@Delete("delete from user where id = #{id}")
int deleteUser(@Param("id") int id);
}
注意:必須要將接口註冊綁定到核心配置文件中
關於@Param() 註解
- 基本類型的參數或者String類型,需要加上
- 引用類型不需要加
- 在SQL中引用的是@Param() 中設定的屬性名
Lombok
-
在IDEA中安裝Lombok插件
-
在項目中導入Lombok的jar包
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.12</version> </dependency>
-
在實體類中加入註解即可使用
@Data:無參構造,get,set,tostring,hashcode,equals @AllArgsConstructor:有參構造 @NoArgsConstructor:無參構造
聯表查詢
多對一處理
多個學生對應一名老師
實體類
@Data
public class Student {
private int id;
private String name;
private Teacher teacher;
}
@Data
public class Teacher {
private int id;
private String name;
}
子查詢,按照查詢嵌套處理
<!-- 思路:
1、查詢所有學生
2、根據學生的tid,尋找對應的老師
-->
<select id="getStudent" resultMap="StudentTeacher">
select * from student
</select>
<resultMap id="StudentTeacher" type="student">
<result property="id" column="id"/>
<result property="name" column="name"/>
<!--複雜的屬性,需要單獨處理,對象:association,集合:collection-->
<association property="teacher" column="tid" javaType="teacher" select="getTeacher"/>
</resultMap>
<select id="getTeacher" resultType="teacher">
select * from teacher where id = #{id}
</select>
連接查詢,按照結果嵌套處理
<!-- 按照結果嵌套處理-->
<select id="getStudent" resultMap="StudentTeacher">
select s.id sid, s.name sname, t.name tname
from student s,teacher t
where s.tid = t.id
</select>
<resultMap id="StudentTeacher" type="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<association property="teacher" javaType="teacher">
<result property="name" column="tname"/>
</association>
</resultMap>
一對多處理
一個老師擁有多名學生
實體類:
@Data
public class Teacher {
private int id;
private String name;
//一個老師擁有多名學生
private List<Student> students;
}
@Data
public class Student {
private int id;
private String name;
private int tid;
}
子查詢,按照查詢嵌套處理
<select id="getTeacher" resultMap="TeacherStudent">
select * from teacher where id = #{tid}
</select>
<resultMap id="TeacherStudent" type="teacher">
<result property="id" column="id"/>
<result property="name" column="name"/>
<collection property="students" column="id" javaType="ArrayList" ofType="student" select="getStudentByTeacherID"/>
</resultMap>
<select id="getStudentByTeacherID" resultType="student">
select * from student where tid = #{tid}
</select>
連接查詢,按照結果嵌套處理
<select id="getTeacher" resultMap="TeacherStudent">
select s.id sid, s.name sname, t.name tname, t.id tid
from student s, teacher t
where s.tid = t.id and t.id=#{tid}
</select>
<resultMap id="TeacherStudent" type="teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<!-- javaType="" 指定屬性的類型
集合中的泛型信息,我們使用ofType獲取
-->
<collection property="students" ofType="student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="tid" column="tid"/>
</collection>
</resultMap>
小結:
- 關聯 - association 【多對一】
- 集合 - collection 【一對多】
- javaType & ofType
- JavaType 用來指定實體類中屬性的類型
2. ofType 用來指定映射到List或者集合中的pojo類型,泛型中的約束類型
- JavaType 用來指定實體類中屬性的類型
注意點
- 保證SQL的可讀性,儘量保證通俗易懂
- 注意一對多和多對一中,屬性名和字段的問題
動態SQL
什麼是動態SQL:動態SQL就是指根據不同的條件生成不同的SQL語句
可以根據不同條件進行SQL拼接。
if
choose ( when, otherwise)
trim (where, set)
foreach
where&if
<select id="queryBlog" parameterType="map" resultType="blog">
select * from blog
<where>
<if test="title != null">
and title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</where>
</select>
choose ( when, otherwise)
相當於Java中的switch語句
<select id="chooseBlog" parameterType="map" resultType="blog">
select * from blog
<where>
<choose>
<when test="title != null">
and title = #{title}
</when>
<when test="author">
and author = #{author}
</when>
<otherwise>
views = #{views}
</otherwise>
</choose>
</where>
</select>
trim(set,where)
<update id="updateBlog" parameterType="map">
update blog
<set>
<if test="title != null">
title = #{title},
</if>
<if test="auther != null">
auther = #{auther}
</if>
</set>
where id = #{id}
</update>
trim標籤可自定義格式,實現where與set標籤功能
foreach
<!-- select * from blog where 1=1 and (id=1 or id=2 or id=3) -->
<select id="foreachBlog" parameterType="map" resultType="blog">
select * from blog
<where>
<foreach collection="ids" item="id" open="and (" close=")" separator="or">
id = #{id}
</foreach>
</where>
</select>
SQL片段
有的時候,我們可能會將一些功能的部分抽取出來,方便複用!
-
使用SQL標籤抽取公共的部分
<sql id="if-title-author"> <if test="title != null"> and title = #{title} </if> <if test="author != null"> and author = #{author} </if> </sql>
-
在需要使用的地方使用include標籤引用即可
<select id="queryBlog" parameterType="map" resultType="blog"> select * from blog <where> <include refid="if-title-author"></include> </where> </select>
注意
- 最好基於單表來定義SQL片段
- 不要存在where標籤
緩存
- 什麼是緩存
- 存在內存中的臨時數據。
- 將用戶經常查詢的數據放在緩存(內存)中,用戶去查詢數據就不用從磁盤上(關係型數據庫數據文件)查詢,從緩存中查詢,從而提高查詢效率,解決了高併發系統的性能問題。
- 爲什麼使用緩存
- 減少和數據庫的交互次數,減少系統開銷,提高系統效率。
- 什麼樣的的數據能使用緩存?
- 經常查詢並且不經常改變的數據。
Mybatis緩存
- MyBatis包含一個非常強大的查詢緩存特性,它可以非常方便地定製和配置緩存。緩存可以極大地提升查詢效率。
- MyBatis系統中默認定義了兩級緩存:一級緩存和二級緩存
- 默認情況下,只有一級緩存開啓。(SqlSession級別的緩存,也稱爲本地緩存)
- 二級緩存需要手動開啓和配置,是基於namespace級別的緩存。
- 爲了提高擴展性,MyBatis定義了緩存接口Cache。可以通過實現Cache接口來自定義二級緩存
一級緩存
- 一級緩存也叫本地緩存:SqlSession
- 與數據庫同一次會話期間查詢到的數據會放到本地緩存中。
- 以後如果需要獲取相同的數據,可以直接從緩存中拿,不用再查詢數據庫;
緩存失效的情況:
-
查詢不同的東西
-
增刪改操作,可能會改變原來的數據,所以必定會刷新緩存
-
查詢不同的Mapper.xml
-
手動清理緩存。
sqlSession.clearCache(); //手動清理緩存
小結:
一級緩存默認是開啓的,只在一次SqlSession中有效,也就是拿到連接到關閉連接這個區間段。
一級緩存相當於是一個map.
二級緩存
- 二級緩存也叫全局緩存,一級緩存作用域低,所以產生了二級緩存
- 基於namespace級別的緩存,一個名稱空間,對應一個二級緩存;
- 工作機制
- 一個會話查詢一條數據,這個數據就會被放在當前會話的一級緩存中;
- 如果當前會話關閉了,這個會話對應的一級緩存就沒了;但是我們需要的是,會話關閉,一級緩存中的數據被保存到二級緩存中;
- 新的會話查詢信息,就可以從二級緩存中獲取內容;
- 不同的mapper查出的數據會放在自己對應的緩存(map)中;
步驟
-
開啓全局緩存
<!--顯示的開啓全局緩存(默認也是開啓)--> <setting name="cacheEnabled" value="true"/>
-
在要使用二級緩存的Mapper中開啓
<!--在當前Mapper.xml中使用二級緩存--> <cache/>
也可以自定義參數
<!--在當前Mapper.xml中使用二級緩存--> <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
小結:
- 只要開啓了二級緩存,在同一個Mapper下就有效
- 所有的數據都會先放在一級緩存中;
- 只有當會話提交,或者關閉的時候,纔會提交到二級緩存中
緩存順序
查詢順序
- 先看二級緩存中有沒有
- 再看一級緩存中有沒有
- 查詢數據庫