緩存可以將數(shù)據(jù)保存在內存中,是互聯(lián)網系統(tǒng)常常用到的。目前流行的緩存服務器有 MongoDB、Redis、Ehcache 等。緩存是在計算機內存上保存的數(shù)據(jù),讀取時無需再從磁盤讀入,因此具備快速讀取和使用的特點。
和大多數(shù)持久化框架一樣,MyBatis 提供了一級緩存和二級緩存的支持。默認情況下,MyBatis 只開啟一級緩存。
一級緩存是基于 PerpetualCache(MyBatis自帶)的 HashMap 本地緩存,作用范圍為 session 域內。當 session flush(刷新)或者 close(關閉)之后,該 session 中所有的 cache(緩存)就會被清空。
在參數(shù)和 SQL 完全一樣的情況下,我們使用同一個 SqlSession 對象調用同一個 mapper 的方法,往往只執(zhí)行一次 SQL。因為使用 SqlSession 第一次查詢后,MyBatis 會將其放在緩存中,再次查詢時,如果沒有刷新,并且緩存沒有超時的情況下,SqlSession 會取出當前緩存的數(shù)據(jù),而不會再次發(fā)送 SQL 到數(shù)據(jù)庫。
由于 SqlSession 是相互隔離的,所以如果你使用不同的 SqlSession 對象,即使調用相同的 Mapper、參數(shù)和方法,MyBatis 還是會再次發(fā)送 SQL 到數(shù)據(jù)庫執(zhí)行,返回結果。
本節(jié)示例基于《第一個MyBatis程序》一節(jié)的代碼實現(xiàn)。
在 WebsiteMapper 類中添加 selectWebsiteById 方法,代碼如下。
public Website selectWebsiteById(int id);
WebsiteMapper.xml 中添加相應的映射 SQL 語句,代碼如下。
<select id="selectWebsiteById" resultType="net.biancheng.po.Website"> SELECT * FROM website WHERE id=#{id} </select>
測試代碼如下。
package net.biancheng.test; import java.io.IOException; import java.io.InputStream; import java.util.List; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.apache.log4j.Logger; import net.biancheng.po.Website; public class Test { public static Logger logger = Logger.getLogger(Test.class); public static void main(String[] args) throws IOException { InputStream config = Resources.getResourceAsStream("mybatis-config.xml"); // 根據(jù)配置文件構建 SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(config); SqlSession ss = ssf.openSession(); Website site = ss.selectOne("net.biancheng.mapper.WebsiteMapper.selectWebsiteById", 1); logger.debug("使用同一個sqlsession再執(zhí)行一次"); Website site2 = ss.selectOne("net.biancheng.mapper.WebsiteMapper.selectWebsiteById", 1); // 請注意,當我們使用二級緩存的時候,sqlSession調用了 commit方法后才會生效 ss.commit(); logger.debug("現(xiàn)在創(chuàng)建一個新的SqlSeesion對象在執(zhí)行一次"); SqlSession ss2 = ssf.openSession(); Website site3 = ss2.selectOne("net.biancheng.mapper.WebsiteMapper.selectWebsiteById", 1); // 請注意,當我們使用二級緩存的時候,sqlSession調用了 commit方法后才會生效 ss2.commit(); } }
輸出結果如下。
DEBUG [main] - ==> Preparing: SELECT * FROM website WHERE id=? DEBUG [main] - ==> Parameters: 1(Integer) DEBUG [main] - <== Total: 1 DEBUG [main] - 使用同一個sqlsession再執(zhí)行一次 DEBUG [main] - 現(xiàn)在創(chuàng)建一個新的SqlSeesion對象在執(zhí)行一次 DEBUG [main] - ==> Preparing: SELECT * FROM website WHERE id=? DEBUG [main] - ==> Parameters: 1(Integer) DEBUG [main] - <== Total: 1
從運行結果可以看出,第一個 SqlSession 實際只發(fā)生過一次查詢,而第二次查詢就從緩存中取出了,也就是 SqlSession 層面的一級緩存。為了克服這個問題,我們往往需要配置二級緩存,使得緩存在 SqlSessionFactory 層面上能夠提供給各個 SqlSession 對象共享。
二級緩存是全局緩存,作用域超出 session 范圍之外,可以被所有 SqlSession 共享。
一級緩存緩存的是 SQL 語句,二級緩存緩存的是結果對象。
1)MyBatis 的全局緩存配置需要在 mybatis-config.xml 的 settings 元素中設置,代碼如下。
<settings></settings>
2)在 mapper 文件(如 WebMapper.xml)中設置緩存,默認不開啟緩存。需要注意的是,二級緩存的作用域是針對 mapper 的 namescape 而言,即只有再次在 namescape 內(net.biancheng.WebsiteMapper)的查詢才能共享這個緩存,代碼如下。
<mapper namescape="net.biancheng.WebsiteMapper"> <!-- cache配置 --> <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true" /> ... </mapper>
以上屬性說明如下。
屬性 | 說明 |
---|---|
eviction | 代表的是緩存回收策略,目前 MyBatis 提供以下策略。
|
flushInterval | 刷新間隔時間,單位為毫秒,這里配置的是 100 秒刷新,如果省略該配置,那么只有當 SQL 被執(zhí)行的時候才會刷新緩存。 |
size | 引用數(shù)目,正整數(shù),代表緩存最多可以存儲多少個對象,不宜設置過大。設置過大會導致內存溢出。這里配置的是 1024 個對象。 |
readOnly | 只讀,默認值為 false,意味著緩存數(shù)據(jù)只能讀取而不能修改,這樣設置的好處是可以快速讀取緩存,缺點是沒有辦法修改緩存。 |
3)在 mapper 文件配置支持 cache 后,如果需要對個別查詢進行調整,可以單獨設置 cache,代碼如下。
<select id="getWebsiteList" resultType="net.biancheng.po.Website" usecache="true"> ... </select>
對于 MyBatis 緩存僅作了解即可,因為面對一定規(guī)模的數(shù)據(jù)量,內置的 Cache 方式就派不上用場了,并且對查詢結果集做緩存并不是 MyBatis 所擅長的,它專心做的應該是 SQL 映射。對于緩存,采用 OSCache、Memcached 等專門的緩存服務器來做更為合理。