天天看點

MyBatis架構:緩存機制,mybatis整合ehcache

mybatis緩存機制

簡介

MyBatis 包含一個非常強大的查詢緩存特性,

它可以非常友善地配置和定制。緩存可以極大的提升查詢效率。

MyBatis系統中預設定義了兩級緩存。

一級例子

= openSession.getMapper(EmpMapper.class);
      Emp empById = mapper.getEmpById(3);
      System.out.println(empById);
      Emp empById2 = mapper.getEmpById(3);
      System.out.println(empById==empById2);
結果是:
隻發了一條sql語句,兩個emp的記憶體位址是一樣的
DEBUG 05-19 11:55:59,176 ==>  Preparing: select eid id,ename name,email,gender from emp where eid= ?   (BaseJdbcLogger.java:145) 
DEBUG 05-19 11:55:59,199 ==> Parameters: 3(Integer)  (BaseJdbcLogger.java:145) 
DEBUG 05-19 11:55:59,220 <==      Total: 1  (BaseJdbcLogger.java:145) 
Emp [eid=3, ename=鳳, email=jane.com, gender=女, dept=null]
true      

一級,二級緩存機制

/*
   * mybatis的緩存機制
   * 分為兩級緩存:
   *   一級緩存:(本地緩存):sqlSession級别的緩存,sqlsession級别的一個Map
   *       一級緩存是一直開啟的,我們關閉不了
   *       與資料庫同一次會話期間查詢到的資料會放在本地緩存中,
   *       以後如果需要擷取相同的資料,直接從緩存中拿,
   *       沒有必要再去查詢資料庫
   *     
   *     一級緩存失效的情況:
   *     就是沒有使用到目前一級緩存的情況,效果就是查詢相同的資料還需要向資料庫發出sql語句
   *     1.sqlsession不同了
   *     2.sqlsession相同,查詢條件不同了,因為目前一級緩存裡面沒有這個資料
   *     3.sqlsession相同,兩次查詢之間執行了增删改操作,因為這個增删改的操作可能對目前的資料有影響
   *     4.sqlsession相同,手動清除了一級緩存,調用了openSession.clearCache();
   * 
   *   二級緩存:(全局緩存)
   *       這是基于namespace級别的緩存,一個namespace就對應一個二級緩存
   *     機制原理:
   *     1.一個會話,查詢一條資料,這個資料就會被放在目前會話的一級緩存中
   *     2.如果會話關閉,一級緩存中的資料就會被儲存到二級緩存中
   *         新的會話查詢資料,就可以參照二級緩存的内容
   *     3.sqlsession會話在EmpMapper中查詢了Emp資訊
   *             在DeptMapper中查詢了Dept資訊
   *             這兩個不同的緩存資訊是不能互相使用的
   *         不同的namespace查出的資料會放在自己對應的緩存中(就是map)
   *     效果:資料會從二級緩存中擷取
   *     注意:查出的資料會預設先放在一級緩存中的,
   *       隻有會話送出或者關閉後,一級緩存中的資料才會轉移到二級緩存中
   *       是以測試的時候,兩個不同的會話調用同一個資料想使用緩存
   *       必須在第二個會話調用前關閉第一個會話
   *     如何使用:
   *         1.開啟全局二級緩存配置,其實預設是開啟的,
   *           但是我們預設是将自己需要的設定都寫上
   *           <setting name="cacheEnabled" value="true"/>
   *         2.哪一個映射檔案需要使用二級緩存
   *           在映射檔案加上<cache></cache>标簽
   *       3.因為緩存使用到序列化技術,每個POJO都要實作序列化接口
   * 
   *   使用緩存的一些小設定:
   *     1.cacheEnabled=true
   *             false:關閉二級緩存,一級緩存是一直可以使用的
   *     2.每一個select标簽都有useCache="true";的設定
   *                     false:就是不使用二級緩存,一級緩存還是可以使用的
   *     3.每一個增删改标簽都有預設的flushCache="true":(一級緩存,二級緩存都會被清除)
   *           這個屬性讓增删改執行完成後清除緩存
   *           flushCache="true":一級緩存,二級緩存都會被清除)
   *         而查詢标簽預設的flushCache="false",
   *         而查詢标簽将flushCache="true",那麼每次查詢後就會清空緩存,緩存時沒有被使用的
   *     4.手動調用的sqlSession.clearCache(),隻是清除目前的一級緩存,二級不會清除
   *     5.全局設定還有這樣一個參數:localCacheScope:本地緩存的作用域
   *         有兩個取值:
   *           SESSION:一級緩存,目前會話的所有資料儲存在會話緩存中;
   *           STATEMENT:沒有一級緩存了,相當于禁用了一級緩存
   */
  
  @org.junit.Test
  public void test9() throws IOException
  {
    String resource = "mybatis.xml"; 
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    try
    {
      SqlSession openSession1 = sqlSessionFactory.openSession();
      SqlSession openSession2 = sqlSessionFactory.openSession();
      EmpMapper mapper1 = openSession1.getMapper(EmpMapper.class);
      EmpMapper mapper2 = openSession2.getMapper(EmpMapper.class);
      
      Emp empById = mapper1.getEmpById(7);
      System.out.println(empById);
      
      openSession1.close();
      Emp empById2 = mapper2.getEmpById(7);
      System.out.println(empById2);
      openSession2.close();
      
    }finally
    {
    }
  }      
<cache eviction="FIFO" flushInterval="10000" readOnly="false" size="1024" ></cache>
  <!-- 
    eviction:緩存回收政策:
        LRU – 最近最少使用的:移除最長時間不被使用的對象。 
        FIFO – 先進先出:按對象進入緩存的順序來移除它們。 
        SOFT – 軟引用:移除基于垃圾回收器狀态和軟引用規則的對象。 
        WEAK – 弱引用:更積極地移除基于垃圾收集器狀态和弱引用規則的對象。 
        預設的是 LRU。 
    flushInterval:緩存重新整理間隔
            緩存多長時間清空一次,預設不清空,設定一個毫秒值
    readOnly:是否隻讀,取值是布爾值
      true:隻讀:mybatis認為所有從緩存中擷取資料的操作都是隻讀的,不會修改資料
            mybatis為了加快擷取速度,直接将資料在緩存中的引用交給使用者
            這樣速度是快了,但是這樣不安全
      false:非隻讀,mybatis覺得擷取的資料可能被修改
            是以就先會利用序列化和反序列化的技術克隆一份新的資料給你
            這樣安全,但是速度慢
    size:緩存存放多少個元素
    type:指定自定義的全類名,自定義的類需要實作Cache接口
   -->      

POJO

public class Emp implements Serializable
{
  /**
   * 
   */
  private static final long serialVersionUID = 1L;
  private Integer eid;
  private String ename;
  private String email;
  private String gender;
  private Dept dept;      
結果:
可以發現,第二次拿到的結果不是發sql得到的,而是
DEBUG 05-20 12:51:52,960 Cache Hit Ratio [bean.EmpMapper]: 0.5  (LoggingCache.java:62) 
直接從二級緩存裡面擷取,擷取了兩次,第二次擷取成功,是以後面的數字是1/2=0.5

DEBUG 05-20 12:51:52,791 Cache Hit Ratio [bean.EmpMapper]: 0.0  (LoggingCache.java:62) 
DEBUG 05-20 12:51:52,805 ==>  Preparing: select eid id,ename name,email,gender from emp where eid= ?   (BaseJdbcLogger.java:145) 
DEBUG 05-20 12:51:52,871 ==> Parameters: 7(Integer)  (BaseJdbcLogger.java:145) 
DEBUG 05-20 12:51:52,940 <==      Total: 1  (BaseJdbcLogger.java:145) 
Emp [eid=7, ename=jane, email=jane.com, gender=男, dept=null]
DEBUG 05-20 12:51:52,960 Cache Hit Ratio [bean.EmpMapper]: 0.5  (LoggingCache.java:62) 
Emp [eid=7, ename=jane, email=jane.com, gender=男, dept=null]      

緩存原理

原理示意圖

MyBatis架構:緩存機制,mybatis整合ehcache

實作

MyBatis架構:緩存機制,mybatis整合ehcache

執行順序

緩存的順序:二級緩存----->一級緩存------>資料庫

mybatis整合ehcache

首先需要導入三個包

ehcache需要的jar包

​​

​ehcache-core-2.6.8.jar​

​​ ehcache允許需要的日志包

​slf4j-api-1.6.1.jar​

​​

​slf4j-log4j12-1.6.2.jar​

​ 有着三個包後我們就可以寫自己的實作類,實作Cache接口就行

但是自己寫實作類好麻煩呀,它是一個成熟的項目,應該自己寫實作的

是以mybatis就幫你寫好了,官網的git那裡有,下載下傳好後導入相應的jar包

​mybatis-ehcache-1.0.3.jar​

​ 根據官方文檔說明,它需要在映射檔案配置好,還有需要一個ehcache.xml檔案就可以使用ehcache了

EmpMapper映射檔案配置

<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>      
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
 <!-- 磁盤儲存路徑 -->
 <diskStore path="E:\new Java\STSworkspace\TempData\ehcache" />
 
 <defaultCache 
   maxElementsInMemory="1000" 
   maxElementsOnDisk="10000000"
   eternal="false" 
   overflowToDisk="true" 
   timeToIdleSeconds="120"
   timeToLiveSeconds="120" 
   diskExpiryThreadIntervalSeconds="120"
   memoryStoreEvictionPolicy="LRU">
 </defaultCache>
</ehcache>
 
<!-- 
屬性說明:
l diskStore:指定資料在磁盤中的存儲位置。
l defaultCache:當借助CacheManager.add("demoCache")建立Cache時,EhCache便會采用<defalutCache/>指定的的管理政策
 
以下屬性是必須的:
l maxElementsInMemory - 在記憶體中緩存的element的最大數目,數字就是記憶體中資料的條數 
l maxElementsOnDisk - 在磁盤上緩存的element的最大數目,若是0表示無窮大
l eternal - 設定緩存的elements是否永遠不過期。如果為true,則緩存的資料始終有效,如果為false那麼還要根據timeToIdleSeconds,timeToLiveSeconds判斷
l overflowToDisk - 設定當記憶體緩存溢出的時候是否将過期的element緩存到磁盤上
 
以下屬性是可選的:
l timeToIdleSeconds - 當緩存在EhCache中的資料前後兩次通路的時間超過timeToIdleSeconds的屬性取值時,這些資料便會删除,預設值是0,也就是可閑置時間無窮大
l timeToLiveSeconds - 緩存element的有效生命期,預設是0.,也就是element存活時間無窮大
 diskSpoolBufferSizeMB 這個參數設定DiskStore(磁盤緩存)的緩存區大小.預設是30MB.每個Cache都應該有自己的一個緩沖區.
l diskPersistent - 在VM重新開機的時候是否啟用磁盤儲存EhCache中的資料,預設是false。
l diskExpiryThreadIntervalSeconds - 磁盤緩存的清理線程運作間隔,預設是120秒。每個120s,相應的線程會進行一次EhCache中資料的清理工作
l memoryStoreEvictionPolicy - 當記憶體緩存達到最大,有新的element加入的時候, 移除緩存中element的政策。預設是LRU(最近最少使用),可選的有LFU(最不常使用)和FIFO(先進先出)
 -->      
<cache-ref namespace="bean.EmpMapper"/>