概述
什麼是緩存
緩存是在計算機內存上進行保存的數據,其特點是將數據保存在內存當中。
爲什麼使用緩存
緩存在讀取的時候不需要再從磁盤讀入,因此具有快速讀取和使用的特點。
什麼時候適合用緩存
如果緩存命中率高,可以極大提升系統性能。如果緩存的命中率比較低,就沒有使用緩存的必要。因此關鍵在於存儲的內容訪問的命中率.
比較適用於:經常查詢但是不經常改變的,數據的正確與否對最終結果的影響不大時。
不適用於:經常改變的數據,數據的正確與否對最終結果的影響比較大時。
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)