1 Mybatis緩存
- Mybatis的緩存,包括一級緩存和二級緩存,一級緩存是默認使用的。二級緩存需要手動開啓。
- 一級緩存指的就是sqlsession,在sqlsession中有一個數據區域,是map結構,這個區域就- 是一級緩存區域。一級緩存中的key是由sql語句、條件、statement等信息組成一個唯一值。一級緩存中的value,就是查詢出的結果對象。
- 二級緩存指的就是同一個namespace下的mapper,二級緩存中,也有一個map結構,這個區域就是一級緩存區域。一級緩存中的key是由sql語句、條件、statement等信息組成一個唯一值。一級緩存中的value,就是查詢出的結果對象。
2 一級緩存
2.1 原理
public class Demo1 {
private SqlSession session;
@Test
public void before() throws IOException {
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
session = factory.openSession();
//通過會話獲取 dao 接口
UserMapper mapper = session.getMapper(UserMapper.class);
/*
* 默認,一級緩存 session 是開啓的
* 保存、刪除、更新,一級緩存會自動清空,下次查詢會執行sql
* */
User user1 = mapper.findUserById(1);
System.out.println(user1);
// 保存用戶
mapper.save(new User("張飛","1",null,"四川"));
System.out.println("-------------------");
User user2 = mapper.findUserById(1);
System.out.println(user2);
}
}
3 二級緩存
- 開啓二級緩存總開關
- UserMapper 配置二級緩存
- User序列化
- 測試
@Test
public void test() throws IOException {
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession session1 = factory.openSession();
SqlSession session2 = factory.openSession();
SqlSession session3 = factory.openSession();
//通過會話獲取 dao 接口
UserMapper mapper1 = session1.getMapper(UserMapper.class);
UserMapper mapper2 = session2.getMapper(UserMapper.class);
UserMapper mapper3 = session3.getMapper(UserMapper.class);
User user1 = mapper1.findUserById(1);
System.out.println(user1);
//session關閉後,纔會提交到二級緩存
session1.close();
User user2 = mapper2.findUserById(1);
System.out.println(user2);
}
- 測試2
@Test
public void test() throws IOException {
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession session1 = factory.openSession();
SqlSession session2 = factory.openSession();
SqlSession session3 = factory.openSession();
//通過會話獲取 dao 接口
UserMapper mapper1 = session1.getMapper(UserMapper.class);
UserMapper mapper2 = session2.getMapper(UserMapper.class);
UserMapper mapper3 = session3.getMapper(UserMapper.class);
User user1 = mapper1.findUserById(1);
System.out.println(user1);
//session關閉後,纔會提交到二級緩存
session1.close();
// 保存,二級緩存清空
mapper3.save(new User("劉備", "1", null, "四川"));
session3.commit();
session3.close();
User user2 = mapper2.findUserById(1);
System.out.println(user2);
session2.close();
}
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.Connection@1ad70640]
DEBUG [main] - ==> Preparing: SELECT * FROM user WHERE id = ?;
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <== Total: 1
User [id=1, username=王五, sex=2, birthday=null, address=上海]
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.Connection@1ad70640]
DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.Connection@1ad70640]
DEBUG [main] - Returned connection 450299456 to pool.
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Checked out connection 450299456 from pool.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.Connection@1ad70640]
DEBUG [main] - ==> Preparing: INSERT INTO user (username,sex,birthday,address) VALUE (?,?,?,?);
DEBUG [main] - ==> Parameters: 劉備(String), 1(String), null, 四川(String)
DEBUG [main] - <== Updates: 1
DEBUG [main] - Committing JDBC Connection [com.mysql.jdbc.Connection@1ad70640]
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.Connection@1ad70640]
DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.Connection@1ad70640]
DEBUG [main] - Returned connection 450299456 to pool.
DEBUG [main] - Cache Hit Ratio [com.tzb.mapper.UserMapper]: 0.0
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Checked out connection 450299456 from pool.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.Connection@1ad70640]
DEBUG [main] - ==> Preparing: SELECT * FROM user WHERE id = ?;
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <== Total: 1
User [id=1, username=王五, sex=2, birthday=null, address=上海]
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.Connection@1ad70640]
DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.Connection@1ad70640]
DEBUG [main] - Returned connection 450299456 to pool.
Process finished with exit code 0
3.1 mapper.xml中配置禁用某個方法緩存
<select id="findUserById" parameterType="int" resultType="com.tzb.model.User" useCache="false">
SELECT * FROM user WHERE id = #{id};
</select>
3.2 刷新緩存
<!--默認,插入、更新、刪除會清空二級緩存
flush 爲 false ,這些操作就不會清空二級緩存-->
<insert id="save" parameterType="com.tzb.model.User" flushCache="false">
<!-- 這裏的佔位寫模型的屬性-->
INSERT INTO user (username,sex,birthday,address)
VALUE (#{username},#{sex},#{birthday},#{address});
</insert>
4 mybatis 整合ehcache緩存
- Mybatis本身是一個持久層框架,它不是專門的緩存框架,所以它對緩存的實現不夠好,不能支持分佈式。
- Ehcache是一個分佈式的緩存框架。
4.1 整合思路
Cache是一個接口,它的默認實現是mybatis的PerpetualCache
。如果想整合mybatis的二級緩存,那麼實現Cache接口即可。
- 添加 jar
- ehcache配置文件
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<diskStore path="java.io.tmpdir"/>
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
maxElementsOnDisk="10000000"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
<persistence strategy="localTempSwap"/>
</defaultCache>
</ehcache>
- 設置映射文件中cache標籤
4.2 二級緩存應用場景
- 使用場景:對於訪問響應速度要求高,但是實時性不高的查詢,可以採用二級緩存技術。
- 注意:在使用二級緩存的時候,要設置一下刷新間隔(cache標籤中有一個flashInterval屬性)來定時刷新二級緩存,這個刷新間隔根據具體需求來設置,比如設置30分鐘、60分鐘等,單位爲毫秒。
4.3 侷限性
- Mybatis二級緩存對細粒度的數據,緩存實現不好。
場景:
對商品信息進行緩存,由於商品信息查詢訪問量大,但是要求用戶每次查詢都是最新的商品信息,此時如果使用二級緩存,就無法實現當一個商品發生變化只刷新該商品的緩存信息而不刷新其他商品緩存信息,因爲二級緩存是mapper級別的,當一個商品的信息發送更新,所有的商品信息緩存數據都會清空。
解決此類問題,需要在業務層根據需要對數據有針對性的緩存。
比如可以對經常變化的 數據操作單獨放到另一個namespace的mapper中。