不定期補充、修正、更新;歡迎大家讨論和指正
目錄
- 傳統的JDBC
- MyBatis
-
- 基本使用
-
- 傳參問題
- 全局配置檔案
- 結果映射
-
- 多表查詢
- 動态SQL
-
- if标簽
- choose标簽
- foreach标簽
- 緩存
-
- 一級緩存
- 二級緩存
- 自定義緩存
- 源碼分析
- 整合Spring
- MyBatisPlus
傳統的JDBC
回顧以前資料庫的連接配接和操作,我們使用的是原生的Java AP來實作,大緻的步驟為
- 配置賬号、密碼、Driver、url成功連接配接資料庫
- 通過連結Connection擷取PreparedStatement類
- 編寫SQL語句,将SQL語句放入PreparedStatement中預編譯
- 填充占位符
- 執行SQL語句,如果是查詢SQL,還需要用ResultSet類來接收結果集
- 關閉資源等收尾工作
以下表為例

插入
@Override
public void insert(Jobs jobs) {
Connection conn = null;
PreparedStatement ps = null;
try {
conn = JdbcUtils.getConnection();//JdbcUtils是自己封裝的工具類,裡面包含擷取連接配接,關閉連接配接的功能
//就是上一篇部落格中CURD->insert->封裝 的getconnection()和closeResource()方法
String sql = "INSERT INTO `jobs`(`job_id`,`job_title`,`min_salary`,`max_salary`)VALUE(?,?,?,?);";
ps = conn.prepareStatement(sql);//預編譯
ps.setString(1,jobs.getJob_id());//填充占位符
ps.setString(2,jobs.getJob_title());
ps.setInt(3,jobs.getMin_salary());
ps.setInt(4,jobs.getMax_salary());
ps.execute();//執行
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
JdbcUtils.closeResource(conn,ps);//關閉連接配接
}
}
删除
@Override
public void deleteByID(String ID) {
Connection conn = null;
PreparedStatement ps = null;
try {
conn = JdbcUtils.getConnection();
String sql = "DELETE FROM `jobs` WHERE `job_id`=?;";
ps = conn.prepareStatement(sql);
ps.setString(1,ID);
ps.execute();
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
JdbcUtils.closeResource(conn,ps);
}
}
查詢
@Override
public Jobs getByID(String ID) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
String sql = "SELECT * FROM `jobs` WHERE `job_id` = ?;";
ps = conn.prepareStatement(sql);
ps.setString(1,ID);
rs = ps.executeQuery();
if (rs.next()) {
Jobs jobs = new Jobs();
jobs.setJob_id(rs.getString(1));
jobs.setJob_title(rs.getString(2));
jobs.setMin_salary(rs.getInt(3));
jobs.setMax_salary(rs.getInt(4));
return jobs;
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
JdbcUtils.closeResource(conn,ps,rs);
}
return null;
在使用傳統的JDBC中,我們可以發現諸多不便
-
CURD很多步驟相同,連接配接資料庫,擷取PreparedStatement等。
解決方法:提出公共部分作于模闆
-
将sql語句寫死到java代碼中,如果sql 語句修改,需要重新編譯java代碼,不利于系統維護。
解決方法:利用反射從外部檔案擷取SQL語句。
- 對于查詢語句,接收結果集的過程十分繁瑣
-
資料庫連結建立、釋放頻繁造成系統資源浪費進而影響系統性能
解決方法:利用資料庫連接配接池
盡管有一些簡化的工具比如Dbutils和JDBCTemplate等,但能做的也很有限。
随後市面上就出現了許多持久層架構來簡化開發和操作,如MyBatis、Hibernate、TopLink、Guzz、jOOQ、Spring Data。如今MyBatis占主流,同時也是主流MVC架構——SSM(Spring + SpringMVC + MyBatis)重要的一環。
MyBatis
什麼是 MyBatis?
MyBatis 是一款優秀的持久層架構,它支援自定義 SQL、存儲過程以及進階映射。MyBatis 免除了幾乎所有的 JDBC 代碼以及設定參數和擷取結果集的工作。MyBatis 可以通過簡單的 XML 或注解來配置和映射原始類型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 對象)為資料庫中的記錄。
–摘自官網
MyBatis 本是apache的一個開源項目iBatis, 2010年這個項目由apache software foundation 遷移到了google code,并且改名為MyBatis 。2013年11月遷移到Github。
iBATIS一詞來源于“internet”和“abatis”的組合,是一個基于Java的持久層架構。iBATIS提供的持久層架構包括SQL Maps和Data Access Objects(DAOs)
–摘自百度百科
MyBatis官方幫助文檔
基本使用
導入Mybatis
jar包:mybatis
Maven依賴
<!-- Maven -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
<!-- 之前用的3.4.6執行後會抛出一堆Warning,查了之後說要降級jdk或者更新mybatis的版本 3.5.5親測沒有這些警告-->
</dependency>
mysql驅動
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.37</version>
</dependency>
每個基于 MyBatis 的應用都是以一個 SqlSessionFactory 的執行個體為核心的。SqlSessionFactory 的執行個體可以通過 SqlSessionFactoryBuilder 獲得。
而 SqlSessionFactoryBuilder 則可以從 XML 配置檔案或一個預先配置的 Configuration 執行個體來建構出 SqlSessionFactory 執行個體。
根據官方所說,SqlSessionFactory是整個MyBatis的核心,SqlSessionFactory通過SqlSessionFactoryBuilder擷取,而SqlSessionFactoryBuilder可以通過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>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED"><!--是否使用連接配接池-->
<!--連接配接資料庫的基本四個資訊-->
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--将mapper注冊到mybatis中,後面會講,很重要的一步,先注釋掉-->
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
</mappers>
</configuration>
接下來就可以擷取SqlSessionFactory執行個體了,通過SqlSessionFactory可以擷取SqlSession執行個體,而SqlSession才是真正與資料庫互動的東西。
為了後續友善使用,可以寫成工具類
package com.jojo.mybatis;
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 {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
在測試前先把主配置檔案中的mapper映射注釋掉,因為這是官方給的例子,我們并沒有該mapper需要映射,否則會報錯
成功擷取SqlSession,但不代表能成功連接配接到資料庫,就算連接配接資料庫的賬号密碼是錯誤的,在這時也不會提示
利用SqlSession向資料庫做CURD操作,仍以下表為例

ORM(對象關系映射),建立實體類,用于後面接收結果集
按照原生JDBC,接下來就是寫SQL語句了,MyBatis提供在XML中編寫SQL語句的形式
<?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="org.mybatis.example.BlogMapper">
<select id="selectBlog" resultType="Blog">
select * from Blog where id = #{id}
</select>
</mapper>
- namespece:命名空間。在大型項目中,可能存在大量的SQL語句,這時候為每個SQL語句起一個唯一的辨別(ID)就變得并不容易了。為了解決這個問題,在MyBatis中,可以為每個映射檔案起一個唯一的命名空間,這樣定義在這個映射檔案中的每個SQL語句就成了定義在這個命名空間中的一個ID。隻要我們能夠保證每個命名空間中這個ID是唯一的,即使在不同映射檔案中的語句ID相同,也不會再産生沖突了。命名空間在之前版本的MyBatis中是可選的,這樣容易引起混淆是以毫無益處。現在命名空間則是必須的,且易于簡單地用更長的完完全限定名來隔離語句。
- id:全局唯一不解釋
- resultType:接收結果集的類
- #{id}:占位符以該形式編寫
命名空間是為了防止id沖突,這裡簡單使用完全不怕,但既然是必須的,就先建立一個接口
接下來很重要的一步,将做CURD的mapper在主配置檔案中注冊
在這裡很容易遇到找不到該mapper路徑的異常,因為Maven一般隻讀取resource下的xml檔案,而mapper一般是放在src目錄下
是以需要在pom.xml添加以下配置
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
接下來就可以通過sqlSession向資料庫進行CURD操作了
因為getById标簽目前全局唯一,直接擷取即可
為了避免标簽沖突,安全的做法是先擷取命名空間所綁定的接口
這樣不僅更安全,可讀性好、傳參也友善
經過簡單的使用,可以發現MyBatis很好地解決了原生JDBC的弊端:SQL語句在外部檔案中編寫,降低耦合性、結果集的接收MyBatis自動完成,隻需要指定接收結果集的類即可,使得我們可以專注于SQL語句的構造。
接下來把剩下的CURD寫完,不過在實作之前導入log4j日志功能,我們隻能通過SqlSession是否操作資料庫成功,但是如果出錯,僅僅通過SqlSession很難進行排錯。
Log4j是Apache的一個開源項目,通過使用Log4j,我們可以控制日志資訊輸送的目的地是控制台、檔案、GUI元件,甚至是套接口伺服器、NT的事件記錄器、UNIX Syslog守護程序等;我們也可以控制每一條日志的輸出格式;通過定義每一條日志資訊的級别,我們能夠更加細緻地控制日志的生成過程。最令人感興趣的就是,這些可以通過一個配置檔案來靈活地進行配置,而不需要修改應用的代碼。
導入依賴
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
建立log4j的配置檔案(log4j.properties,最好放在resource下)
這裡是從Log4j的配置與使用詳解直接扒下來的
# Global logging configuration
# 設定日志輸出級别以及輸出目的地,可以設定多個輸出目的地,開發環境下,日志級别要設定成DEBUG或者ERROR
# 前面寫日志級别,逗号後面寫輸出目的地:我自己下面設定的目的地相對應,以逗号分開
# log4j.rootLogger = [level],appenderName1,appenderName2,…
log4j.rootLogger=DEBUG,CONSOLE,LOGFILE
#### 控制台輸出 ####
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
# 輸出到控制台
log4j.appender.CONSOLE.Target = System.out
# 指定控制台輸出日志級别
log4j.appender.CONSOLE.Threshold = DEBUG
# 預設值是 true, 表示是否立即輸出
log4j.appender.CONSOLE.ImmediateFlush = true
# 設定編碼方式
log4j.appender.CONSOLE.Encoding = UTF-8
# 日志輸出布局
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
# 如果日志輸出布局為PatternLayout 自定義級别,需要使用ConversionPattern指定輸出格式
log4j.appender.CONSOLE.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %5p (%c:%L) - %m%n
#### 輸出錯誤資訊到檔案 ####
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
# 指定輸出檔案路徑
#log4j.appender.LOGFILE.File =F://Intellij idea/logs/error.log
log4j.appender.LOGFILE.File =./logs/error.log
#日志輸出到檔案,預設為true
log4j.appender.LOGFILE.Append = true
# 指定輸出日志級别
log4j.appender.LOGFILE.Threshold = ERROR
# 是否立即輸出,預設值是 true,
log4j.appender.LOGFILE.ImmediateFlush = true
# 設定編碼方式
log4j.appender.LOGFILE.Encoding = UTF-8
# 日志輸出布局
log4j.appender.LOGFILE.layout = org.apache.log4j.PatternLayout
# 如果日志輸出布局為PatternLayout 自定義級别,需要使用ConversionPattern指定輸出格式
log4j.appender.LOGFILE.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
有了日志功能,可以了解到SQL完整的執行過程,友善調試
在接口制定CURD方法
插入
對于增删改操作,SqlSession預設是不會自動送出事務的,需要手動送出
更改
删除
查詢全部
查詢多個結果,雖然傳回值是集合,但在SQL語句傳回值設定為集合中元素的類型就行
傳參問題
細心的朋友可以發現在以上CURD傳參的形式都有所不同
在MyBatis中,當傳參隻有一個時,在SQL語句寫的參數名可以随意
比如删除操作,因為不會産生歧義,名字怎麼取都沒關系
當傳的參數有多個時,可以傳map,SQL語句的#{}中的變量名需要和map中的key相同
也可以通過Java Bean傳遞多個參數,SQL語句的#{}中的變量名和實體類的屬性相同
還有是使用@param注解的形式,這裡不進行示範
MyBatis(四)SQL語句中參數傳遞的五種方法
還有需要注意的是 ${},#{}的差別
在很多地方,${} 的作用是直接取值,這種方式容易産生SQL注入的危險(以預編譯的方式将參數設定到sql中)
而#{} 運作結果會是一個?占位符 ,和使用PreparedStatement目的一緻
當然${} 并不是一無是處,可以用于分庫分表排序等原生jdbc不支援占位符的地方,例如
select * from ${month}_salary order by ${name}
全局配置檔案
全局配置檔案的屬性設定,完全可以看官方文檔
mybatis-配置
這裡使用幾個常用的
屬性(properties)
主要用來擷取外部檔案的内容,比如連接配接資料庫的資訊可以單獨放到另一個檔案,然後通過properties引入
也可以直接在标簽内設定,但是優先級還是檔案中的高
類型别名(typeAliases)
類型别名可為 Java 類型設定一個縮寫名字。 它僅用于 XML 配置,意在降低備援的全限定類名書寫。
比如之前mapper用到類時寫的是全路徑比較長
取别名
當需要某個包下有很多類需要取别名時,可以以包為機關為其下的所有類取别名
類的别名為類名(無視大小寫)
環境配置(environments)
MyBatis 可以配置成适應多種環境,這種機制有助于将 SQL 映射應用于多種資料庫之中, 現實情況下有多種理由需要這麼做。例如,開發、測試和生産環境需要有不同的配置;或者想在具有相同 Schema 的多個生産資料庫中使用相同的 SQL 映射。還有許多類似的使用場景。
不過要記住:盡管可以配置多個環境,但每個 SqlSessionFactory 執行個體隻能選擇一種環境,是以要在< environments default=“development” >标簽内選擇具體的環境
每套環境内都要設定transactionManager和dataSource,具體内容自己看
設定(settings)
這是 MyBatis 中極為重要的調整設定,它們會改變 MyBatis 的運作時行為。自行了解,後續使用時會提到。
結果映射
在前面select語句中,對于複雜的結果集我們常用JavaBean 或 POJO來接收,也就是resultType=‘JavaBean’,雖然看似是ResultMap完成了結果集的映射,但實際上是ResultMap完成了這些事。MyBatis 會在幕後自動建立一個 ResultMap,再根據屬性名來映射列到 JavaBean 的屬性上。結果映射是 MyBatis 最強大的特性,如果你對其了解透徹,許多複雜的映射問題都能迎刃而解。 resultType 和 resultMap 之間隻能同時使用一個。
什麼時候用ResultMap呢,當資料庫的字段和javaBean屬性字段不一樣時就可以使用(資料庫字段命名一般為job_id,而Java是駝峰式命名)
之前屬性名和資料庫字段是一一對應的,是以直接可以用resultType接收
假如改成駝峰式命名,我們來看看還能接收到結果集嗎
可以看到能查詢到結果,但是沒接收到結果集
這種情況下容易解決,我們可以在全局配置檔案中的setting标簽内開啟駝峰命名自動映射
也可以利用SQL文法中的as取别名(别忘了關掉駝峰命名自動映射,不然不知道有沒有成功)
還有一種方法就是用resultMap作映射了
resultMap标簽有三個屬性
這裡使用兩個子标簽就足夠了(id和result的作用一樣)
在資料庫字段和屬性名一一對應或者駝峰轉換後對應,直接用resultType就行,用resultMap就多次一舉了
但是對于多表查詢就不得不用resultMap了,在此之前先了解resultMap标簽内所有的屬性
多表查詢
在先前我們已經有了一張job表
現在根據job_id與另一張employee表進行多表查詢(為了友善點,這裡隻考慮以下四個屬性,以及job表的job_title屬性)
而這兩張表結合呈現一對多的關系(以job表為主表,一個job可以有多個employee),而反過來就是多對一的關系(以employee為主表),是以使用resultMap時要對兩種關系分别進行實作。
構造實體類
因為job表可以查出多個employees,是以要為其添加一個清單類型的屬性
重寫下Employees的toString方法,友善後面檢視
多對一
接下來先以簡單的多對一關系進行實作,以下面查詢結果為例
映射檔案,在主配置檔案注冊這些正常流程就不說了
先構造select語句
再構造resultMap,因為查詢的主表是Employees,是以type為Employees
這段沒什麼好說的,都是Employees的屬性,按照普通的列名-屬性名進行映射就行
重點是association标簽,關聯(association)元素處理“有一個”類型的關系
association标簽下又包含四個屬性,不過隻用關心property和javaType
簡單來說
property:你想将另一張表查詢的結果集放到哪個屬性下,還記得在Employees表下的Jobs jobs屬性嗎,是以這裡property=’jobs’
javaType:接收另一張表結果集的JavaBean
接下來隻要完成另一張表自己的列名-屬性名的映射就行了
test
查詢成功
在該例中,我們完全可以将多表查詢等價為嵌套子查詢,即先查詢jobs表得到結果,在此結果上再查詢employees表
是以我們可以修改成另一種形式
實作查詢jobs表和employees的SQL語句,jobs直接傳回結果,employees表還需要進行映射處理
同樣重點在于association标簽
property和javaType和上面一樣
column和select的功能如下
column:連接配接鍵的列名,這裡column是必選項,不像前面可以略去,因為要知道通過哪個共同的鍵進行查詢
select:也是必選,不然怎麼獲得嵌套查詢的結果
同樣成功查詢
一對多
當jobs作為主表時,通過job_id查詢employees,顯然構成了一對多的關系,但是大部分操作都相似,連SQL語句都是一樣的
接下來是關鍵點resultMap,association标簽用于處理“有一個”類型的關聯,而處理“有很多個”類型的關聯就需要用到collection集合标簽了,collection标簽的屬性和association标簽大部分相同,需要注意的是collection傳回的類型,因為傳回的是List,是以javaType得是ArrayList類型(可忽略),而ofType填才是List中元素的類型
在jobs表中實作列印方法(本來不用這麼麻煩,但是隻取了幾個字段,其他字段就不列印了)
成功擷取
關于多表查詢的結果映射還是比較令人頭大,可以參考以下視訊,以及強烈建議參考官方文檔
【狂神說Java】Mybatis最新完整教程IDEA版通俗易懂
動态SQL
動态sql是指在進行sql操作的時候,傳入的參數對象或者參數值,根據比對的條件,有可能需要動态的去判斷是否為空,循環,拼接等情況,是比較常用的功能。
比如在一個頁面中的表單有幾個傳輸參數(學到MyBatis應該都學過JaveWeb了)
根據傳輸的參數動态地構造SQL語句
執行SQL語句
原文:JDBC實作動态查詢
可以發現使用原生的JDBC十分的繁瑣,而且還容易漏掉空格逗号造成錯誤,是以MyBatis提供了強大的動态SQL,讓我們擺脫這種麻煩。
if标簽
if标簽是比較常用的功能,最常見情景是根據條件包含 where 子句的一部分
比如想查詢工資大于minSalary,小于maxSalary範圍的字段
僅傳入minSalary
根據日志資訊可以看到成功拼接了SQL語句(maxSalary為空)
在此基礎上查詢工資低于20000的
其實這裡寫的拼接語句有很大的漏洞,假如minSalary為空但maxSalary不為空
就會拼接成錯誤的SQL語句導緻報錯
一種解決方法是在主SQL中添加where 1=1(保證後續的語句肯定執行),拼接SQL統一用邏輯連接配接符開頭
這樣SQL語句就不會出錯了
另一種方法是使用trim标簽,這個就自己去官網看。
choose标簽
choose标簽和swith-case語句類似,隻會進入一個分支
比如我們可以根據job_id或job_title查詢字段,如果一個屬性存在,另一個就屬性就不使用
為了規範點,以後查詢條件都放在where标簽内
隻用job_title查詢(when标簽)
同時用兩個屬性查詢,可以看到當排在前面的分支符合後,就算後面有其他分支符合條件也不會進入
預設情況(otherwise标簽)
foreach标簽
foreach常見使用場景是對集合進行周遊(尤其是在建構 IN 條件語句的時候)
foreach使用難點比if和choose複雜些,主要在于參數的選擇,foreach标簽内有以下幾個參數
- item:表示集合中每一個元素進行疊代時的别名。(必選)
- index:指定一個名字,表示在疊代過程中每次疊代到的位置。(可選)
- open:表示該語句以什麼開始(如果是 in 條件語句,以’('開始 )(可選)
- separator:表示在每次進行疊代之間以什麼符号作為分隔符(如果是 in 條件語句,以’,'作為分隔符)(可選)
- close:表示該語句以什麼結束(如果 in 條件語句,以’)'開始 )(可選)
使用 foreach 标簽時,最關鍵、最容易出錯的是 collection 屬性,該屬性是必選的,但在不同情況下該屬性的值是不一樣的,主要有以下 3 種情況:
- 如果傳入的是單參數且參數類型是一個 List,collection 屬性值為 list。
- 如果傳入的是單參數且參數類型是一個 array 數組,collection 的屬性值為 array。
- 如果傳入的參數是多個,需要把它們封裝成一個 Map,當然單參數也可以封裝成 Map。Map 的 key 是參數名,collection 屬性值是傳入的 List 或 array 對象在自己封裝的 Map 中的 key。
- 摘自MyBatis foreach标簽
比如傳入List,通過多個job_id查詢多個字段
另外提一下,我們知道形參不同就可以函數重載,但在mybatis這是不允許的,因為标簽id必須得唯一
Mybatis的XxxMapper.xml中能否配置重載方法
Mybatis的mapper接口函數重載問題
緩存
MyBatis包含一個非常強大的查詢緩存特性,它可以非常友善地定制和配置緩存。緩存可以極大地提升查詢效率
MyBatis預設定義了兩級緩存:一級緩存和二級緩存
一級緩存
一級緩存是預設開啟的(SqlSession級别的緩存,也稱為本地緩存)
如下,當我們先後分别查詢同一字段,因為第一次查詢的結構仍在緩存中,是以并不需要再次從資料庫查詢,大大增加了高并發的效率,因為我們知道資料從記憶體中讀取遠比從磁盤IO讀取來的快
示範需要用到日志功能,這裡不使用log4j,使用MyBatis自帶的日志功能換換口味,在主配置檔案的setting标簽開啟就行
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
一級緩存在幾種情況下會失效,官方的給的文檔說到select語句的結果會被緩存,但是insert、update、delete語句會重新整理緩存
如下,在兩次查詢同一字段中,我們還更新了字段,盡管更新的字段和要查詢的字段無關,MyBatis為了保證ACID原則也會再次從資料庫查詢
對此我們可以看各個标簽内的屬性,對于select語句,預設使用緩存
對于增删改操作,沒有useCache屬性,并且flushCache預設為true,是以一執行就會重新整理緩存
二級緩存
二級緩存也叫全局緩存,是基于namespace級别的緩存,一個命名空間對應一個緩存(二級緩存并不是來解決一級緩存失效的問題的)
一個會話查詢一個緩存,查詢的資料會存放在目前會話的一級緩存中,如果該會話關閉,其緩存也會消除。
但我們可以使其儲存在二級緩存中,新的會話查詢就可以從二級緩存讀取,不同的mapper查出的資料會放在自己對應的緩存中
要啟用全局的二級緩存,需要現在主配置檔案開啟。
因為二級緩存的作用域是namespace,也就是mapper映射檔案,是以哪個映射檔案需要二級緩存,在該mapper映射檔案中添加< cache >即可
該标簽下有五個屬性
- eviction(清除政策):緩存清空政策,預設為LRU
- LRU – 最近最少使用:移除最長時間不被使用的對象。
- FIFO – 先進先出:即隊列,按對象進入緩存的順序來移除它們。
- SOFT – 軟引用:基于垃圾回收器狀态和軟引用規則移除對象。
- WEAK – 弱引用:更積極地基于垃圾收集器狀态和弱引用規則移除對象
- flushInterval(重新整理間隔)屬性可以被設定為任意的正整數,設定的值應該是一個以毫秒為機關的合理時間量。 預設情況是不設定,也就是沒有重新整理間隔,緩存僅僅會在調用語句時重新整理。
- size(引用數目)屬性可以被設定為任意正整數,要注意欲緩存對象的大小和運作環境中可用的記憶體資源。預設值是 1024。
- readOnly(隻讀)屬性可以被設定為 true 或 false。隻讀的緩存會給所有調用者傳回緩存對象的相同執行個體。 是以這些對象不能被修改。這就提供了可觀的性能提升。而可讀寫的緩存會(通過序列化)傳回緩存對象的拷貝。 速度上會慢一些,但是更安全,是以預設值是 false。
- type(自定義緩存)除了上述自定義緩存的方式,也可以通過實作你自己的緩存,或為其他第三方緩存方案建立擴充卡,來完全覆寫緩存行為。
建立兩個會話,在第一次會話關閉後,仍然查詢相同的字段,很明顯可以看到兩次查詢的對象是相同的。
關閉二級緩存後
自定義緩存
MyBatis畢竟不是專門做緩存的,是以在一些複雜的情況下就需要用其他更強大的緩存,比如EhCache、Redis。自行了解
MyBatis自定義緩存——Redis實作
MyBatis自定義緩存——EhCache實作
源碼分析
MyBatis底層源碼解析 (詳細)
整合Spring
目前來說,Mybatis常與Spring、SpringMVC一起組成SSM架構,也是目前主流的MVC架構來完成一個JavaWeb項目的開發,是以學習整合Mybatis和Spring(SpringMVC是Spring下的子產品,整合Spring就行)是十分必要的。
使用 Spring IoC 可以有效的管理各類的 Java 資源,達到即插即拔的功能;通過 Spring AOP 架構,資料庫事務可以委托給 Spring 管理,消除很大一部分的事務代碼,配合 MyBatis 的高靈活、可配置、可優化 SQL 等特性,完全可以建構高性能的大型網站。
毫無疑問,MyBatis 和 Spring 兩大架構已經成了 Java 網際網路技術主流架構組合,它們經受住了大資料量和大批量請求的考驗,在網際網路系統中得到了廣泛的應用。使用 MyBatis-Spring 使得業務層和模型層得到了更好的分離,與此同時,在 Spring 環境中使用 MyBatis 也更加簡單,節省了不少代碼,甚至可以不用 SqlSessionFactory、 SqlSession 等對象,因為 MyBatis-Spring 為我們封裝了它們。
–《Java EE 網際網路輕量級架構整合開發》
篇幅原因這裡不進行實作
Spring:
Java學習_Spring_IoC
Java學習_Spring_AOP
MyBatis-Spring
MyBatis 與 Spring 整合
MyBatisPlus
- 占坑