java ssm框架 緩存_SSM框架之MyBatis3專題4:查詢緩存

查詢緩存的使用,主要是為了提高查詢訪問速度。將用戶對同一數據的重復查詢過程簡化,不再每次均從數據庫中查詢獲取結果數據,從而提高訪問速度。

MyBatis的查詢緩存機制,根據緩存區的作用域(聲明周期)可劃分為兩種:一級查詢緩存和二級查詢緩存。

1 一級查詢緩存

MyBatis一級查詢緩存是基于org.apache.ibatis.cache.impl.PerpetualCache類的HashMap本地緩存,其作用域是SqlSession。在同一個SqlSession中兩次執行相同的sql查詢語句,第一次執行完畢后,會將查詢結果寫入到緩存中,第二次會從緩存中直接獲取數據,而不再到數據庫中進行查詢,從而提高查詢效率。

當一個SqlSession結束后,該SqlSession中的一級緩存也就不存在了。MyBatis默認一級查詢緩存是開啟狀態的,且不能夠關閉。

一級緩存執行原理示意圖:

b2c960e2bb140fc59c087495e6e2e7c0.png

一級緩存執行流程圖:

178cda45436b6f1ebb98aa8e28fd06ff.png

1.1 一級查詢緩存的存在性證明

1.1.1 修改測試類:

// 證明一級緩存是存在的

@Test

public void test01() {

Student student = dao.selectById(3);

System.out.println(student);

Student student2 = dao.selectById(3);

System.out.println(student2);

}

1.1.2 查看控制臺:

執行完畢后,發現只是執行了一次從DB中的查詢,第二次的結果是直接輸出的。說明,第二次是從SqlSession緩存中讀取的。

6ea68c91d0885af8670c0eb137d96269.png

1.2 從緩存讀取數據的依據是Sql的id

一級緩存緩存的是相同的Sql映射id的查詢結果,而非相同Sql語句的查詢結果。因為myBatis內部對于查詢緩存,無論是以及一級緩存還是二級查詢緩存,其底層均是使用一個HashMap實現的:key為Sql的id相關內容,value為從數據庫中查詢出的結果。

1.2.1 修改映射文件

在映射文件中對于某一個標簽進行完全復制,然后修改一下這個SQL映射的id。也就是說,這兩個SQL映射除了id不同,其他均相同,即查詢結果肯定是相同的。

select * from t_student where id=#{jjj}

select * from t_student where id=#{jjj}

1.2.2 修改Dao接口

Student selectById(int id);

Student selectById2(int id);

1.2.3 修改測試類

@Test

public void test02() {

Student student = dao.selectById(3);

System.out.println(student);

Student student2 = dao.selectById2(3);

System.out.println(student2);

}

1.2.4 查看控制臺

查看控制臺,發現第二次查詢結果與第一次的完全相同,但是第二次查詢并沒有從緩存中讀取數據,而是直接從DB中進行的查詢。這是因為從緩存讀取數據的依據是查詢SQL的映射id,而非查詢結果。

0ac70388e3d6201b9f6af4616be19df3.png

1.3 增刪改對一級查詢緩存的影響

1.3.1 修改測試類

增、刪、改操作,無論是否進行提交sqlSession.commit(),均會清空一級查詢緩存,使得查詢再次從DB中select。

@Test

public void test03() {

Student student = dao.selectById(3);

System.out.println(student);

dao.deleteById(4);

Student student2 = dao.selectById(3);

System.out.println(student2);

}

1.3.2 查看控制臺

93abb21970060020a4a0a9bf4c4d9133.png

2 內置二級緩存查詢

MyBatis查詢緩存的作用域是根據映射文件mapper的namespace劃分的,相同的namespace的mapper查詢數據存放在同一個緩存區域中。不同的namespace中的數據互不干擾。無論是一級緩存還是二級緩存,都是按照namespace進行分別存放的。

但一級、二級緩存的不同之處在于,SqlSession一旦關閉,則SqlSession中的數據將不存在,即一級緩存就不復存在。而二級緩存的生命周期會與整個應用同步,與SqlSession是否關閉無關。

使用二級緩存的目的,不是共享查詢,因為MyBatis從緩存中讀取數據的依據是SQL的id,而非查詢出的對象。所以,二級緩存中的數據不是為了在多個查詢之間共享(所有查詢中只要查詢結果中存在該對象,就直接從緩存中讀取,這是對數據的共享,Hibernate中的緩存就是為了共享,但是MyBatis的不是。),而是為了延長該查詢結果的保存時間,提高系統性能。

MyBatis內置的二級緩存為:org.apache.ibatis.cache.impl.PerpetualCache。

2.1 二級緩存用法

二級查詢緩存的使用很簡單,只需要完成兩步即可:

2.1.1 實體序列化

要求查詢結果所涉及到的實體類要實現Java.io.Serializable接口。若該實體類存在父類,或者其具有域屬性,則父類與域屬性也要實現序列化接口。

public class Student implements Serializable{

private static final long serialVersionUID = -6979637229268429997L;

private Integer id;

private String name;

private int age;

private double score;

}

2.1.2 mapper映射中添加標簽

在mapper映射文件的標簽中添加子標簽。

2.1.3 二級緩存的配置

為標簽添加一些相關屬性設置,可以對二級緩存的運行性能進行控制。當然,若不指定設置,則均保持默認值。

readOnly="true" size="512"/>

1、eviction:逐出策略。當二級緩存中的對象達到最大值時,就需要通過逐出策略將緩存中的對象溢出緩存。默認為LRU。常用的策略有:

FIFO:First In First Out,先進先出;

LRU:Least Recently Used,未被使用時時間最長;

2、flushInterval:刷新緩存的時間間隔,單位毫秒。這里的刷新緩存即清空緩存。一般不指定,即當執行增刪改時刷新緩存;

3、readOnly:設置緩存中的數據是否為只讀,只讀的緩存會給所有調用者返回緩存對象的相同實例,因此這些對象是不能夠被修改的,這提供了很重要的性能優勢。但是讀寫的緩存會返回緩存對象的拷貝。這會慢一些,但是很安全,因此默認值為false。

4、size:二級緩存中可以存放的最多對象個數。默認為1024個;

2.2 二級查詢緩存的存在性證明

對于映射文件中的同一個查詢,肯定是同一個namespace中的查詢(因為就是同一個查詢)。在一次查詢后,將SqlSession關閉,再進行一次相同查詢,發現并沒有到DB中進行select查詢,說明二級查詢緩存是存在的。

2.2.1 修改測試類

@Test

public void test04() {

sqlSession = MyBatisUtils.getSqlSession();

dao = sqlSession.getMapper(IStudentDao.class);

//第一次查詢

Student student = dao.selectById(3);;

System.out.println(student);

//關閉SqlSession

sqlSession.close();

sqlSession = MyBatisUtils.getSqlSession();

dao = sqlSession.getMapper(IStudentDao.class);

//第二次查詢

Student student2 = dao.selectById(3);

System.out.println(student2);

}

2.2.2 查看控制臺

Cache Hit Ratio表示緩存命中率。開啟二級緩存后,每次執行一次查詢,系統都會計算一次二級緩存的命中率。第一次查詢也是先從緩存中查詢,只不過緩存中一定是沒有的。所以會再從DB中查詢。由于二級緩存中不存在該數據,所以命中率為0。但是第二次查詢是從二級緩存中讀取的,所以這一次的命中率為1/2 = 0.5。當然,若有第三次查詢的話,則命中率會為1/3=0.66。

9157f8a2f402f7300ac92d7812fec2c0.png

2.3 增刪改對二級查詢緩存的影響

2.3.1 默認對緩存的刷新

增刪改操作,無論是否進行提交sqlSession.commit(),均會清空一級、二級查詢緩存,使得查詢再次從DB中select。

1、修改測試類:

@Test

public void test05() {

sqlSession = MyBatisUtils.getSqlSession();

dao = sqlSession.getMapper(IStudentDao.class);

//第一次查詢

Student student = dao.selectById(3);;

System.out.println(student);

//關閉SqlSession

sqlSession.close();

sqlSession = MyBatisUtils.getSqlSession();

dao = sqlSession.getMapper(IStudentDao.class);

//刪除一個對象

dao.deleteById(4);

//第二次查詢

Student student2 = dao.selectById(3);

System.out.println(student2);

}

2、查看控制臺:

注意,在第二次查詢時的緩存命中率為0.5,但還是從DB中查詢了。這說明在緩存中與該查詢相對應的key是存在的,但是其value被清空。而value被清空的原因是前面執行了對象DB的增刪改操作,所以不會從緩存中直接將null值返回,而是從DB中進行查詢。

053d3588e7abc4ff03e678f90f83ff0d.png

2.3.2 設置增刪改操作不刷新二級緩存

若要使得某個增刪改操作不清空二級緩存,則需要在其或者或者中添加屬性flushCache="false",默認值為true。

delete from t_student where id = #{xxx}

0d6e2f3141d7c94aa98a720c2f5d94a1.png

2.4 二級緩存的關閉

二級緩存默認為開啟狀態。若要將其關閉,則需要進行相關配置。

根據關閉的范圍大小,可以分為全局關閉和局部關閉。

2.4.1 全局關閉

所謂全局關閉是指,整個應用的二級緩存全部關閉,所以查詢均不使用二級緩存。全局開關設置在主配置文件的全局設置中,該屬性為cacheEnabled,設置為false,則關閉;設置為true,則開啟,默認值為true。即,二級緩存默認是開啟的。

2.4.2 局部關閉

所謂局部關閉是指,整個應用的二級緩存是開啟的,但是只是對于某個查詢,不使用二級緩存。此時可以單獨只關閉該標簽的二級緩存。

在該要關閉的二級緩存的標簽中,將其屬性useCache設置為false,即可關閉該查詢的二級緩存。該屬性默認為true,即每個查詢的二級緩存默認是開啟的。

select * from t_student where id=#{jjj}

2.5 二級緩存的使用原則

2.5.1 只能夠在一個命名空間下使用二級緩存

由于二級緩存中的數據時基于namespace的,即不同的namespace中的數據互不干擾。在多個namespace中若均存在對同一個表的操作,那么這多個namespace中的數據可能就會出現不一致現象。

2.5.2 在單表上使用二級緩存

如果一個表與其他表有關聯關系,那么就非常有可能存在多個namespace對同一數據的操作。而不同的namespace中的數據互不干擾,所以有可能出現這多個namespace中的數據不一致現象。

2.5.3 查詢多于修改時使用二級緩存

在查詢操作遠遠多于增刪改操作的情況下可以使用二級緩存。因為任何增刪改操作都將刷新二級緩存,對二級緩存的頻繁刷新就會降低系統性能。

3 ehcache二級查詢緩存

mybatis的特長是SQL操作,緩存數據管理不是其特長,為了提高緩存的性能,myBatis允許使用第三方緩存產品。ehCache就是其中一種。

注意,使用ehcache二級緩存,實體類無需實現序列化接口。

3.1 導入Jar包

這里需要兩個Jar包:一個為ehcache的核心Jar包,一個是myBatis與ehcache整合的插件Jar包。它們可以從https://github.com/mybatis/ehcache-cache/releases 下面。

fa69ecddeb0d56513ee0266bb440764b.png

解壓該文件,獲取到它們。其中lib下的是ehcache的核心Jar包。

e314fded1fba06b9e6b45e2e536335d3.png

3.2 ehcache.xml

解壓EHCache的核心Jar包ehcache-core-2.6.8.jar,將其中的一個配置文件ehcache.failsafe.xml直接放到項目的src目錄下,并更名為ehcache.xml。

e24ced950e3966a09f1e84160016937c.png

3.2.1 < diskStore/>標簽

指定一個文件目錄,當內存空間不夠,需要將二級緩存中的數據寫到硬盤上時,會寫到這個指定目錄中。其值一般為java.io.tmpdir,表示當前系統的默認文件臨時目錄。

cefc8699868952b23498af96d59ceea7.png

當前系統的默認文件臨時目錄,可以通過System.property()方法查看:

@Test

public void test() {

String path = System.getProperty("java.io.tempdir");

System.out.println(path);

}

3.2.2 < defaultCache/>標簽

maxElementsInMemory="10000"

eternal="false"

timeToIdleSeconds="120"

timeToLiveSeconds="120"

maxElementsOnDisk="10000000"

diskExpiryThreadIntervalSeconds="120"

memoryStoreEvictionPolicy="LRU">

設定緩存的默認屬性數據:

1、maxElementsInMemory:指定該內存緩存可以存放緩存對象的最多個數;

2、eternal:設定緩存對象是否不會過期。若設為true,表示對象永遠不會過期,此時會忽略timeToIdleSeconds與timeToLiveSeconds屬性。默認值為false;

3、timeToldleSeconds:設定允許對象處于空閑狀態的最長時間,以秒為單位。當對象自從最近一次被訪問后,若處于空閑狀態的時間超過了timeToldleSeconds設定的值,這個對象就會過期。當對象過期,ehcache就會將它從緩存中清除。設置值為0,則對象可以無限期地處于空閑狀態。

4、timeToLiveSeconds:設定對象允許存在于緩存中的最長時間,以秒為單位。當對象自從被存放到緩存后,若處于緩存中的時間超過了timeToLiveSeconds設定的值,這個對象就會過期。當對象過期,ehcache就會將它從緩存中清除。設置值為0,則對象可以無限期地存在于緩存中。注意,只有timeToLiveSeconds>=timeToldleSeconds時,才有意義;

5、overflowToDisk:設定為true,表示當緩存對象達到maxElementsInMemory界限時,會將溢出的對象寫到元素指定的硬盤目錄緩存中;

6、maxElementsOnDisk:指定硬盤緩存區可以存放緩存對象的最多個數;

7、diskPersistent:指定當程序結束時,硬盤緩存區中的緩存對象是否做持久化;

8、diskExpiryThreadIntervalSeconds:指定硬盤中緩存對象的失效時間間隔;

9、memoryStoreEvictionPolicy:如果內存緩存區超過限制,選擇移向硬盤緩存區中的對象時使用的策略。支持三種策略:

FIFO:First In First Out,先進先出;

LFU:Less Frequenlty Used,最少使用;

LRU,Least Recently Used,最近最少使用;

3.3 啟用ehcache緩存機制

在映射文件的mapper中的中通過type指定緩存機制為ehcache緩存。默認為myBatis內置的二級緩存org.apache.ibatis.cache.impl.PerpetualCache。

該類在mybatis-ehcache的jar包中可以找到。

98800885f8284ef5f6fca4aadef9128d.png

3.4 ehcache在不同mapper中的個性化設置

在ehcache.xml中設置的屬性值,會對該項目中所有使用ehcache緩存機制的緩存區域起作用。一個項目中可以有多個mapper,不同的mapper有不同的緩存區域。對于不同緩存區域也可進行專門針對于當前區域的個性化設置,可通過指定不同mapper的屬性值來設置。

屬性值的優先級高于ehcache.xml中的屬性值。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/542591.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/542591.shtml
英文地址,請注明出處:http://en.pswp.cn/news/542591.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

matplotlib畫圖_漂亮,超詳細的matplotlib畫圖基礎

來自 | 逐夢erhttps://zhumenger.blog.csdn.net/article/details/106530281本文僅作技術交流&#xff0c;如有侵權&#xff0c;請聯系后臺刪除。數據可視化非常重要&#xff0c;因為錯誤或不充分的數據表示方法可能會毀掉原本很出色的數據分析工作。matplotlib 庫是專門用于開發…

c# 2維數組 取一維_C#| 不同類型的一維數組聲明

c# 2維數組 取一維In the below example, we are declaring an integer array (one dimensional) with following styles: 在下面的示例中&#xff0c;我們聲明具有以下樣式的整數數組(一維) &#xff1a; 1) One dimensional Array declaration with initialization (without…

Java編程經典10道_Java經典編程題50道之十二

企業發放的獎金根據利潤提成&#xff1a;利潤(I)低于或等于10萬元時&#xff0c;獎金可提10%&#xff1b;利潤高于10萬元&#xff0c;低于20萬元時&#xff0c;低于10萬元的部分按10%提成&#xff0c; 高于10萬元的部分 &#xff0c;可提成7.5%&#xff1b;20萬到40萬之間時&am…

RHEL7 單獨安裝圖形 X11

RHEL7 默認是最小化安裝&#xff08;Minimal Install&#xff09;&#xff0c;沒有圖形界面&#xff0c; 我們應該選擇Server with GUI。若已錯過此步驟&#xff0c;我們采用以下方式補充安裝GUI界面。 先配置yum源可以參考我的這篇文章http://blog.itpub.net/27771627/viewspa…

android recycleview長按多選_UI設計中Android和IOS設計差異總結

由于設計師、產品經理使用的移動設備大部分是iPhone&#xff0c;所以在做設計時&#xff0c;容易忽略Android和iOS的差異&#xff0c;按照iOS的規范進行設計&#xff0c;兩端只做一套。只做一套的會存在兩個問題&#xff1a;1、安卓用戶的使用習慣不太適應iOS的設計&#xff0c…

Kotlin程序用于打印JVM版本的Kotlin(打印Java屬性)

Here, we will create a Kotlin program to print Kotlin, JVM version (printing Java properties). As Kotlin can be seen as an upgrade of Java, so we will get all versions of java (JVM) using Kotlin also. 在這里&#xff0c;我們將創建一個Kotlin程序以打印JVM版本…

自定義動畫屬性java_創建酷炫動畫效果的10個JavaScript庫

原標題&#xff1a;創建酷炫動畫效果的10個JavaScript庫1) Dynamics.jsDynamics.js是設計基于物理規律的動畫的重要Java庫。它可以賦予生命給所有包含CSS 和SVG屬性的DOM(文本對象模型)元素&#xff0c;換句話說&#xff0c;Dynamics.js適用于所有Java對象以及一系列其它的元素…

php xlsx里插入圖片_常見的 PHP 面試題和答案分享

如何直接將輸出顯示給瀏覽器&#xff1f;將輸出直接顯示給瀏覽器&#xff0c;我們必須使用特殊標記 <&#xff1f;and&#xff1f;>。PHP 是否支持多重繼承&#xff1f;PHP 只支持單繼承。PHP 的類使用關鍵字 extends 繼承另一個類獲取圖片屬性&#xff08;size, width, …

java調用構造函數中某一個值_Java如何在枚舉的構造函數中調用另一個枚舉值

Java中的枚舉(enum)是一種存儲一組常量值的數據類型。您可以使用枚舉來存儲固定值&#xff0c;例如一周中的天&#xff0c;一年中的月等。您可以使用關鍵字 enum定義枚舉&#xff0c;后跟枚舉的名稱為-enum Days {SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATUR…

python 示例_Python日歷類| yeardatescalendar()方法與示例

python 示例Python Calendar.yeardatescalendar()方法 (Python Calendar.yeardatescalendar() Method) Calendar.yeardatescalendar() method is an inbuilt method of the Calendar class of calendar module in Python. It uses an instance of this class and returns a lis…

js:插入節點appendChild insertBefore使用方法

首先 從定義來理解 這兩個方法&#xff1a; appendChild() 方法&#xff1a;可向節點的子節點列表的末尾添加新的子節點。語法&#xff1a;appendChild(newchild) insertBefore() 方法&#xff1a;可在已有的子節點前插入一個新的子節點。語法 &#xff1a;insertBefore(newchi…

pandas concat_pandas-數據合并-concat(最全參數解釋,含代碼和實例)

pandas中的concat的功能&#xff1a;假設你現在需要將多個數據合并&#xff0c;前提是&#xff1a;這幾個文件列名都一致&#xff0c;也就是說這幾個文件格式完全一樣&#xff0c;只是數據不太一樣&#xff0c;類似于合并多個文件這種&#xff0c;實際數據分析中也會遇到這種情…

java中的de是什么_【轉】java中main函數解析

源地址&#xff1a;http://www.cnblogs.com/xwdreamer/archive/2012/04/09/2438845.html從寫java至今&#xff0c;寫的最多的可能就是主函數public static void main(String[] args) {}但是以前一直都沒有問自己&#xff0c;為什么要這么寫&#xff0c;因為在c語言中就沒有這樣…

JAVA多線程(一)線程安全問題產生的原因

JAVA線程內存與主存間映射示意圖Java內存模型中規定了所有的變量都存儲在主內存中&#xff0c;每條線程還有自己的工作內存&#xff0c;線程的工作內存中保存了該線程使用的變量到主內存副本拷貝&#xff0c;線程對變量的所有操作&#xff08;讀取、賦值&#xff09;都必須在工…

兩頂點的路徑長度為k_計算兩個頂點之間的所有可能路徑

兩頂點的路徑長度為kWhat to Learn? 學什么&#xff1f; How to count all possible paths between two vertices? 如何計算兩個頂點之間的所有可能路徑&#xff1f; In the graph there are many alternative paths from vertex 0 to vertex 4 在圖中&#xff0c;有許多從…

php debug_print_backtrace,php中debug_backtrace、debug_print_backtrace和匿名函數用法實例

本文實例講述了php中debug_backtrace、debug_print_backtrace和匿名函數用法。分享給大家供大家參考。具體分析如下&#xff1a;debug_print_backtrace() 是一個很低調的函數,很少有人注意過它.不過當我們對著一個對象調用另一個對象再調用其它的對象和文件中的一個函數出錯時,…

covariance matrix r語言_時間序列分析|ARIMAX模型分步驟詳解和R中實踐

這是關于時間序列的第N篇文章&#xff0c;本文將介紹ARIMAX模型&#xff0c;簡單來說就是在ARIMA的基礎上增加一個外生變量。ARIMAX和ARIMA相比在理論上沒有太多新的內容&#xff0c;所以本文直接介紹在R里怎么一步一步跑ARIMAX。在閱讀這篇文章前&#xff0c;需要對ARIMA有一定…

linux系統編程之文件與I/O(六):fcntl 函數與文件鎖

2013-05-14 11:26 8290人閱讀 評論(2) 收藏 舉報分類&#xff1a;linux系統編程&#xff08;19&#xff09; 版權聲明&#xff1a;本文為博主原創文章&#xff0c;未經博主允許不得轉載。 一、fcntl函數 功能&#xff1a;操縱文件描述符&#xff0c;改變已打開的文件的屬性 int…

python 使用異常函數_您如何測試Python函數引發異常?

python 使用異常函數This article elaborates on how to implement a test case for a function that raises an exception. 本文詳細介紹了如何為引發異常的函數實現測試用例 。 Consider the following function: 考慮以下功能&#xff1a; import redef check_email_forma…