1. 程式人生 > 實用技巧 >Mybatis——一級快取與二級快取

Mybatis——一級快取與二級快取

關於Mybatis的學習主要參考了狂神的視訊

  1. 一級快取

    (1).使用範圍:從sqlSession會話開始到結束

    (2).使用:預設開啟,無法關閉

    (3).測試使用(需要開啟日誌觀察資料庫的連線情況):

    public static void Maintest(){
        SqlSession sqlSession = Connection.getSqlSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        HashMap map = new HashMap();
        map.put("UId","3180421016");
        List<UserBean> userBeans = userMapper.queryByIf(map);
        map.put("UName","關晨亮");
        //userMapper.updateById(map);
        System.out.println(userMapper.queryByIf(map).get(0).equals(userBeans.get(0)));
        sqlSession.close();
    }
    //result:true,將結果集列印,可以看出兩次結果集列印之間是沒有再做資料庫連線的
    

    (4).快取失效的4種情況:

    • sqlSession不同

      public static void Maintest(){
          SqlSession sqlSession1 = Connection.getSqlSession();
          SqlSession sqlSession2 = Connection.getSqlSession();
          UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
          UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
          HashMap map = new HashMap();
          map.put("UId","3180421016");
          List<UserBean> userBeans = userMapper1.queryByIf(map);
          System.out.println(userBeans);
          sqlSession1.close();
          System.out.println(userBeans.get(0).equals(userMapper2.queryByIf(map).get(0)));
          sqlSession2.close();
      }
      
    • sqlSession相同,兩次查詢操作之間存在增刪改操作

      public static void Maintest(){
          SqlSession sqlSession = Connection.getSqlSession();
          UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
          HashMap map = new HashMap();
          map.put("UId","3180421016");
          List<UserBean> userBeans = userMapper.queryByIf(map);
          map.put("UName","關晨亮");
          userMapper.updateById(map);
          System.out.println(userMapper.queryByIf(map).get(0).equals(userBeans.get(0)));
          sqlSession.close();
      }
      
      //result:false,將結果集列印,可以看出兩次結果集列印之間是有再次做過資料庫連線的
      
    • sqlSession相同,查詢條件不同(此時快取中沒有相關資料)

      public static void Maintest(){
          SqlSession sqlSession1 = Connection.getSqlSession();
          UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
          HashMap map = new HashMap();
          map.put("UId","3180421016");
          List<UserBean> userBeans = userMapper1.queryByIf(map);
          System.out.println(userBeans);
          map.put("UId","2");
          userBeans = userMapper1.queryByIf(map);
          System.out.println(userBeans);
          sqlSession1.close();
      }
      //開啟日誌可以看到,發生了兩次對於資料庫的連線請求
      
    • 通過session.clearCache()主動重新整理快取

      public static void Maintest(){
          SqlSession sqlSession1 = Connection.getSqlSession();
          UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
          HashMap map = new HashMap();
          map.put("UId","3180421016");
          List<UserBean> userBeans = userMapper1.queryByIf(map);
          System.out.println(userBeans);
          sqlSession1.clearCache();
          userBeans = userMapper1.queryByIf(map);
          System.out.println(userBeans);
          sqlSession1.close();
      }
      
  2. 二級快取

    (1).簡介

    • 作用範圍:整個namespace,也就是一個mapper
    • 實現:不同的mapper查出的資料會放在對應的快取(map)中

    (2).使用:

    • 在主配置檔案中顯式地開啟二級快取
    <settings>
        <!--        <setting name="logImpl" value="LOG4J"/>-->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
        <setting name="cacheEnabled" value="true"/>
    </settings>
    
    • 在Mapper.xml中配置(為什麼要開啟readOnly會在後面解釋)
    <cache readOnly="true"/>
    

    <cache
      eviction="FIFO"
      flushInterval="60000"
      size="512"
      readOnly="true"/>
    
    • 測試
    public static void Maintest(){
        SqlSession sqlSession1 = Connection.getSqlSession();
        SqlSession sqlSession2 = Connection.getSqlSession();
        UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
        UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
        HashMap map = new HashMap();
        map.put("UId","3180421016");
        List<UserBean> userBeans = userMapper1.queryByIf(map);
        sqlSession1.close();
        System.out.println(userBeans.get(0).equals(userMapper2.queryByIf(map).get(0)));
        sqlSession2.close();
    }
    /result:true
    

    (3).注意

    • 需要實體序列化

    客戶端訪問了某個能開啟會話功能的資源, web伺服器就會建立一個與該客戶端對應的HttpSession物件,每個HttpSession物件都要站用一定的記憶體空間。如果在某一時間段內訪問站點的使用者很多,web伺服器記憶體中就會積累大量的HttpSession物件,消耗大量的伺服器記憶體,即使使用者已經離開或者關閉了瀏覽器,web伺服器仍要保留與之對應的HttpSession物件,在他們超時之前,一直佔用web伺服器記憶體資源。

    web伺服器通常將那些暫時不活動但未超時的HttpSession物件轉移到檔案系統或資料庫中儲存,伺服器要使用他們時再將他們從檔案系統或資料庫中裝載入記憶體,這種技術稱為Session的持久化。

    將HttpSession物件儲存到檔案系統或資料庫中,需要採用序列化的方式將HttpSession物件中的每個屬性物件儲存到檔案系統或資料庫中;將HttpSession物件從檔案系統或資料庫中裝載如記憶體時,需要採用反序列化的方式,恢復HttpSession物件中的每個屬性物件。所以儲存在HttpSession物件中的每個屬性物件必須實現Serializable介面

    public class UserBean implements Serializable {
        private String UId;
        private String UName;
        private int USet;
        private int UAuth;
        private String UPassword;
        private int UState;
    }
    
    • 必須開啟只讀,否則兩次比較的結果不同

    只讀的快取會給所有呼叫者返回快取物件的相同例項。 因此這些物件不能被修改。這就提供了可觀的效能提升。而可讀寫的快取會(通過序列化)返回快取物件的拷貝。 速度上會慢一些,但是更安全,因此預設值是 false。

    <cache readOnly="true"/>
    
    • 快取優先放在以及會話中,當會話關閉後,快取才會被轉移到二級會話

      public static void Maintest(){
          SqlSession sqlSession1 = Connection.getSqlSession();
          SqlSession sqlSession2 = Connection.getSqlSession();
          UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
          UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
          HashMap map = new HashMap();
          map.put("UId","3180421016");
          List<UserBean> userBeans = userMapper1.queryByIf(map);
          System.out.println(userBeans);
          System.out.println(userBeans.get(0).equals(userMapper2.queryByIf(map).get(0)));
          sqlSession1.close();
          sqlSession2.close();
      }
      //false,因為還沒有關閉就開始比較了
      
      public static void Maintest(){
          SqlSession sqlSession1 = Connection.getSqlSession();
          SqlSession sqlSession2 = Connection.getSqlSession();
          UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
          UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
          HashMap map = new HashMap();
          map.put("UId","3180421016");
          List<UserBean> userBeans = userMapper1.queryByIf(map);
          System.out.println(userBeans);
          sqlSession1.close();
          System.out.println(userBeans.get(0).equals(userMapper2.queryByIf(map).get(0)));
          sqlSession2.close();
      }
      //true,因為是會話關閉之後再比較的
      
    • 對於查詢(select),我們可以使用useCache來選擇是否取消快取;對於增刪改,可以使用flushCache來選擇是否取消更新快取

  3. 快取原理,這邊用狂神的圖來加深理解

  4. 使用ehcache外部快取

    (1).導包

    (2).寫配置檔案.xml

    (3).在主配置檔案中使用:設定cache標籤的type屬性

    注:現在多用redis資料庫