【MyBatis】緩存機制

概述

什麼是緩存

     緩存是在計算機內存上進行保存的數據,其特點是將數據保存在內存當中。

爲什麼使用緩存

    緩存在讀取的時候不需要再從磁盤讀入,因此具有快速讀取和使用的特點。

什麼時候適合用緩存

如果緩存命中率高,可以極大提升系統性能。如果緩存的命中率比較低,就沒有使用緩存的必要。因此關鍵在於存儲的內容訪問的命中率.

比較適用於:經常查詢但是不經常改變的,數據的正確與否對最終結果的影響不大時。

不適用於:經常改變的數據,數據的正確與否對最終結果的影響比較大時。

MyBatis中的緩存

一級緩存

MyBatis默認情況下只開啓一級緩存,一級緩存只是相對於同一個SqlSession而言。

所在在查詢的參數和SQL語句不變的情況下,使用同一個SqlSession對象調用同一個Mapper的方法,往往只執行一次SQL,在第一次進行查詢之後,會將查詢的結果存放到緩存當中,以後如果再次查詢,如果沒有聲明需要刷新,並且緩存也沒有超時的情況下,SqlSession會取出當前緩存的結果,而不是再次發送SQL到數據庫。但是如果是不同的SqlSession對象,他們之間是相互隔離的,所以對於不同的SqlSession對象執行相同的SQL,還是會向數據庫發起緩存。當SqlSession對象消失時,其對應的一級緩存同樣消失。

測試兩個不同的SqlSession對象執行相同的查詢:

    public void testFindById(){
        SqlSession sqlSession1 = factory.openSession();
        IUserDao userDao1 = sqlSession1.getMapper(IUserDao.class);
        System.out.println("第一次查詢");
        User user1 = userDao1.findById(62);
        System.out.println(user1);

        SqlSession sqlSession2 = factory.openSession();
        IUserDao userDao2 = sqlSession2.getMapper(IUserDao.class);
        System.out.println("第二次查詢");
        User user2 = userDao2.findById(62);
        System.out.println(user2);
        sqlSession1.close();
        sqlSession2.close();
        System.out.println(user1==user2);
    }

發現使用不同的SqlSession對象是,執行相同的查詢語句其結果是不同的。

通過下面程序進行測試同一個SqlSession對象執行兩次相同的查詢操作:

public class UserTest {
    private InputStream is;
    private SqlSession sqlSession;
    private IUserDao userDao;

    //初始化進行封裝
    @Before //用於在測試方法執行之前執行
    public void init() throws IOException {
        //1.讀取配置文件,形成字節輸入流
        is = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.獲取SqlSessionFactory
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
        //3.獲取SqlSession對象
        sqlSession = factory.openSession();
        //4.獲取dao的代理對象
        userDao = sqlSession.getMapper(IUserDao.class);
    }

    //close進行封裝
    @After //用於在測試方法執行之後執行
    public void distory() throws IOException {
        //提交事務
        sqlSession.commit();
        //6.關閉資源
        sqlSession.close();
        is.close();
    }

    //測試一級緩存
    @Test
    public void testFindById(){
        System.out.println("第一次查詢");
        User user1 = userDao.findById(62);
        System.out.println(user1);
        System.out.println("第二次查詢");
        User user2 = userDao.findById(62);
        System.out.println(user2);
        System.out.println(user1==user2);
    }

}

運行結果:

可以看出只有第一次查詢向數據庫發送了查詢命令,第二次沒有發送,並且兩次查詢結果得到的對象地址相同。 

如果第一次和第二次查詢之間將SqlSession對象關閉,則緩存消失:


public class UserTest {
    private InputStream is;
    private SqlSession sqlSession;
    private IUserDao userDao;
    private SqlSessionFactory factory;

    //初始化進行封裝
    @Before //用於在測試方法執行之前執行
    public void init() throws IOException {
        //1.讀取配置文件,形成字節輸入流
        is = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.獲取SqlSessionFactory
        factory = new SqlSessionFactoryBuilder().build(is);
        //3.獲取SqlSession對象
        sqlSession = factory.openSession();
        //4.獲取dao的代理對象
        userDao = sqlSession.getMapper(IUserDao.class);
    }

    //close進行封裝
    @After //用於在測試方法執行之後執行
    public void distory() throws IOException {
        //提交事務
        sqlSession.commit();
        //6.關閉資源
        sqlSession.close();
        is.close();
    }

    //測試一級緩存
    @Test
    public void testFindById(){
        System.out.println("第一次查詢");
        User user1 = userDao.findById(62);
        System.out.println(user1);
        //關閉後再次新建sqlSession
        sqlSession.close();
        sqlSession = factory.openSession();
        userDao = sqlSession.getMapper(IUserDao.class);
        System.out.println("第二次查詢");
        User user2 = userDao.findById(62);
        System.out.println(user2);
        System.out.println(user1==user2);
    }

}

 另外有一個方法可以清空緩存:

sqlSession.clearCache();

那麼如果數據庫的數據在查詢過程中發生了改變怎麼辦,MyBatis如何做到數據庫中數據和緩存中同步?當調用SqlSession的修改,添加,刪除,commit(),close() 等方法時,都會將緩存清空。

例如在兩次查詢之間更新數據:


    //測試緩存與數據庫的同步
    @Test
    public void testClearCache(){
        System.out.println("第一次查詢用戶信息");
        User user1 = userDao.findById(62);
        System.out.println(user1);
        System.out.println("更新用戶信息");
        user1.setAddress("河南");
        user1.setQq("1111111");
        userDao.updateUser(user1);
        System.out.println("第二次查詢");
        User user2 = userDao.findById(62);
        System.out.println(user2);
        System.out.println(user1==user2);
    }

二級緩存

它指的是Mybatis中SqlSessionFactory對象的緩存。由同一個SqlSessionFactory對象創建的SqlSession之間共享其緩存。

SqlSessionFactory層面上的二級緩存是不開啓的,二級緩存的開啓需要進行配置,需要在映射XML文件中進行配置。實現二級緩存的時候,MyBatis要求返回的POJO必須是可序列話的,也就是實現Serializeble接口。

二級緩存的使用步驟:
      第一步:讓Mybatis框架支持二級緩存,mybatis-configuration.xml 文件中加入如下代碼:

<!--開啓二級緩存  -->
<settings>    
<setting name="cacheEnabled" value="true"/>
</settings>

第二步:讓當前的映射文件支持二級緩存,在UserMapper.xml中添加:

<cache/> 

 第三步:讓當前的操作支持二級緩存,在select標籤中配置:

useCache和flushCache

mybatis中還可以配置userCache和flushCache等配置項,userCache是用來設置是否禁用二級緩存的,在statement中設置useCache=false可以禁用當前select語句的二級緩存,即每次查詢都會發出sql去查詢,默認情況是true,即該sql使用二級緩存。

下面這種情況是針對每次查詢都需要最新的數據sql,要設置成useCache=false,禁用二級緩存,直接從數據庫中獲取。

<select id="selectUserByUserId" useCache="false" resultType="com.ys.twocache.User" parameterType="int">    
select * from user where id=#{id}
</select>

注意:在mapper的同一個namespace中,如果有其它insert、update、delete操作數據後需要刷新緩存,如果不執行刷新緩存會出現髒讀。

設置statement配置中的flushCache=”true” 屬性,默認情況下爲true,即刷新緩存,如果改成false則不會刷新。使用緩存時如果手動修改數據庫表中的查詢數據會出現髒讀。

<select id="selectUserByUserId" flushCache="true" useCache="false" resultType="com.ys.twocache.User" parameterType="int">    

select * from user where id=#{id}

</select>

一般下執行完commit操作都需要刷新緩存,flushCache=true表示刷新緩存,這樣可以避免數據庫髒讀。所以我們不用設置,默認即可。

現在開啓二級緩存,然後再使用兩個SqlSession對象執行相同的查詢操作:

    //測試二級緩存
    @Test
    public void testFindById(){
        SqlSession sqlSession1 = factory.openSession();
        IUserDao userDao1 = sqlSession1.getMapper(IUserDao.class);
        System.out.println("第一次查詢");
        User user1 = userDao1.findById(62);
        System.out.println(user1);
        sqlSession1.close();

        SqlSession sqlSession2 = factory.openSession();
        IUserDao userDao2 = sqlSession2.getMapper(IUserDao.class);
        System.out.println("第二次查詢");
        User user2 = userDao2.findById(62);
        System.out.println(user2);
        sqlSession2.close();
        System.out.println(user1==user2);
    }

從運行結果可以看出,雖然是兩個不同的SqlSession對象,但是由於開始了二級緩存,第二次進行查找的時候屎直接從緩存中拿到的數據,但是發現兩個user實例並不一樣,這是因爲數據在二級緩存中不是以對象的形式存在的,而是以數據的形式存在的。 

注意:開啓二級緩存,POJO中必須要進行序列化,否則會報錯。

註解方式

對於註解方式中開啓二級緩存可以使用註解:

  第一步:讓Mybatis框架支持二級緩存,mybatis-configuration.xml 文件中加入如下代碼,這一步也可以省略,因爲默認爲開啓狀態:

<!--開啓二級緩存  -->
<settings>    
<setting name="cacheEnabled" value="true"/>
</settings>

第二步:在mapper類中添加註解:

@CacheNamespace(blocking = true)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章