Mybatis的一級快取和二級快取的理解以及用法

  • 2019 年 10 月 3 日
  • 筆記

  程式中為什麼使用快取?
  先了解一下快取的概念:原始意義是指訪問速度比一般隨機存取存儲器快的一種RAM,通常它不像系統主存那樣使用DRAM技術,而使用昂貴但較快速的SRAM技術。對於我們編程來說,所謂的快取,就是將程式
或系統經常要調用的對象(臨時數據)存在記憶體中,一遍其使用時可以快速調用,不必再去創建新的重複的實例。這樣做可以減少系統的開銷,提高效率。
  對快取有了一定的了解以後就知道了使用快取是為了減少和資料庫的交互次數,提高執行效率。那麼下一個問題來了。什麼樣的數據能使用快取,什麼樣的數據不能使用?
  這是我們使用快取必須要明確的事情,實際上適用於快取的數據:經常查詢並且不經常改變的,並且的數據的正確與否對最終結果影響不大的、不適用於快取的數據:經常改變的數據,數據的正確與否對最終
結果影響很大的。
  Mybatis中的一級快取和二級快取到底快取了什麼,快取了以後又有什麼效果,快取的數據什麼時候會被清空?
  一級快取:它指的是Mybatis中sqlSession對象的快取,當我們執行查詢以後,查詢的結果會同時存入到SqlSession為我們提供的一塊區域中,該區域的結構是一個Map,當我們再次查詢同樣的數據,mybatis會
先去sqlsession中查詢是否有,的話直接拿出來用,當SqlSession對象消失時,mybatis的一級快取也就消失了,同時一級快取是SqlSession範圍的快取,當調用SqlSession的修改、添加、刪除、commit(),close等
方法時,就會清空一級快取。
  二級快取:他值得是Mybatis中SqlSessionFactory對象的快取,由同一個SqlSessionFactory對象創建的SqlSession共享其快取,但是其中快取的是數據而不是對象,所以從二級快取再次查詢出得結果的對象與
第一次存入的對象是不一樣的。
  通過簡單的例子來加深理解一級快取和二級快取。

 一級快取

  1.用戶類

public class User implements Serializable{      private Integer id;      private String username;      private Date birthday;      private String sex;      private String address;        get和set方法省略.....  }

    2.Dao層

public interface UserDao {      /**       * 查詢所有的用戶       *       * @return       */      List<User> findAll();      /**       * 根據Id查詢用戶       *       * @return       */      User findById(Integer id);      /**       * 更新用戶       * @param user       */      void updateUser(User user);  }

  3.UserDao.xml映射文件

<mapper namespace="com.example.dao.UserDao">      <select id="findAll" resultType="com.example.domain.User">          SELECT * FROM USER;      </select>      <select id="findById" resultType="com.example.domain.User" parameterType="INT">          SELECT * FROM  USER  WHERE ID = #{ID}      </select>      <update id="updateUser" parameterType="com.example.domain.User">          update USER          <set>              <if test="username != null">username=#{username},</if>              <if test="password != null">birthday=#{birthday},</if>              <if test="sex != null">sex=#{sex},</if>              <if test="address != null">address=#{address},</if>          </set>          where id=#{id}      </update>  </mapper>

  在以上三步中這是mybatis的單表操作,下面通過根據用戶ID查詢用戶的操作來觀察一級快取生效與否的區別  

  4.測試
    (1)  命中一級快取的情況
    測試程式碼:

@Test      public void findByIdTest(){          session = factory.openSession();          userDao = session.getMapper(UserDao.class);          //第一次獲取該用戶          User user1 = userDao.findById(45);          System.out.println(user1);          第二次獲取該用戶          User user2 = userDao.findById(45);          System.out.println(user2);          System.out.println(user1 == user2);          session.close();      }  

測試結果:

  

  (2)對SqlSession進行清除快取的操作,即清楚一級快取,然後再次進行測試。

 @Test      public void findByIdTest(){          session = factory.openSession();          userDao = session.getMapper(UserDao.class);          User user1 = userDao.findById(45);          System.out.println(user1);   //       session.commit(); 調用SqlSession的commit方法清空快取            user1.setUsername("更新用戶");          user1.setAddress("更新地址");          userDao.updateUser(user1);//通過更新SqlSession清空快取          User user2 = userDao.findById(45);          System.out.println(user2);          System.out.println(user1 == user2);          session.close();      }

  清空快取的操作很多,可以都試試。測試結果:

  二級快取  

  再看一下Mybatis二級快取是如何使用的,第一步讓Mybatis框架支援二級快取(在Mybatis的主配置文件中配置),第二步讓當前的映射文件支援二級快取(在Dao.xml映射文件中配置),第三步讓當前的方法支援二級快取(在標籤中配置)。根據這個步驟將上面的查詢用戶的介面通過配置改造為可以支援二級快取的方法。
  1.配置Mybatis框架支援二級快取

<setting name="cacheEnabled" value="true"/>

  2.配置UserDao.xml支援二級快取

 <cache/>

  3.配置查詢的方法支援二級快取

<select id="findById" resultType="com.example.domain.User" parameterType="INT" useCache="true">          SELECT * FROM  USER  WHERE ID = #{ID}     </select>

  4.測試

@Test      public void findByIdTest(){          //第一次查詢 並更新二級快取          SqlSession session1 = factory.openSession();          UserDao userDao1 = session1.getMapper(UserDao.class);          User user1 = userDao1.findById(45);          System.out.println(user1);          session1.commit(); //commit()方法提交二級快取 同時清空一級快取          session1.close();//    //        user1.setUsername("更新用戶");  //        user1.setAddress("更新地址");  //        userDao.updateUser(user1);//通過更新SqlSession清空快取          //第二次查找命中二級快取          SqlSession session2 = factory.openSession();          UserDao userDao2 = session2.getMapper(UserDao.class);          User user2 = userDao2.findById(45);          session2.commit(); //commit()方法提交二級快取 同時清空一級快取          session2.close();//          System.out.println(user2);          System.out.println(user1 == user2);      }

  測試結果:

 

 總結:mybatis的的一級快取是SqlSession級別的快取,一級快取快取的是對象,當SqlSession提交、關閉以及其他的更新資料庫的操作發生後,一級快取就會清空。二級快取是SqlSessionFactory級別的快取,同一個SqlSessionFactory產生的SqlSession都共享一個二級快取,二級快取中存儲的是數據,當命中二級快取時,通過存儲的數據構造對象返回。查詢數據的時候,查詢的流程是二級快取>一級快取>資料庫