MyBatis 默認開啓了一級緩存,一級緩存是在SqlSession 層面進行緩存的。即,同一個SqlSession ,屢次調用同一個Mapper和同一個方法的同一個參數,只會進行一次數據庫查詢,而後把數據緩存到緩衝中,之後直接先從緩存中取出數據,不會直接去查數據庫。java
可是不一樣的SqlSession對象,由於不用的SqlSession都是相互隔離的,因此相同的Mapper、參數和方法,他仍是會再次發送到SQL到數據庫去執行,返回結果。mysql
public static void main(String[] args) { // 自定義的單例SqlSessionFactory模式 SqlSessionFactory factory = SqlSessionFactoryUtil.openSqlSession(); // 得到SqlSession對象 SqlSession sqlSession = factory.openSession(); // 得到dao實體 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); // 進行兩次相同的查詢操做 userMapper.selectByPrimaryKey(1); userMapper.selectByPrimaryKey(1); // 注意,當咱們使用二級緩存時候,sqlSession須要使用commit時候纔會生效 sqlSession.commit(); System.out.println("\n\n============================================================="); // 得到一個新的SqlSession 對象 SqlSession sqlSession1 = factory.openSession(); // 進行相同的查詢操做 sqlSession1.getMapper(UserMapper.class).selectByPrimaryKey(1); // 注意,當咱們使用二級緩存時候,sqlSession須要使用commit時候纔會生效 sqlSession.commit(); }
日誌輸出算法
DEBUG [main] - ooo Using Connection [com.mysql.jdbc.JDBC4Connection@77caeb3e] DEBUG [main] - ==> Preparing: select user_ID, login_name,user_name, user_code, user_type, user_active, organization_ID,user_position,password from user where user_ID = ? DEBUG [main] - ==> Parameters: 1(Integer) TRACE [main] - <== Columns: user_ID, login_name, user_name, user_code, user_type, user_active, organization_ID, user_position, password TRACE [main] - <== Row: 1, ASH-001, 小明, JIKF-001, ADMIN, 1, 0, 銷售員, 1212121212121 DEBUG [main] - <== Total: 1 ============================================================= DEBUG [main] - ooo Using Connection [com.mysql.jdbc.JDBC4Connection@553f17c] DEBUG [main] - ==> Preparing: select user_ID, login_name,user_name, user_code, user_type, user_active, organization_ID,user_position,password from user where user_ID = ? DEBUG [main] - ==> Parameters: 1(Integer) TRACE [main] - <== Columns: user_ID, login_name, user_name, user_code, user_type, user_active, organization_ID, user_position, password TRACE [main] - <== Row: 1, ASH-001, 小明, JIKF-001, ADMIN, 1, 0, 銷售員, 1212121212121 DEBUG [main] - <== Total: 1
能夠發現,第一次的兩個相同操做,只執行了一次數據庫。後來的那個操做又進行了數據庫查詢。sql
爲了克服這個問題,須要開啓二級緩存,是的緩存zaiSqlSessionFactory層面給各個SqlSession 對象共享。默認二級緩存是不開啓的,須要手動進行配置。數據庫
<cache/>
若是這樣配置的話,不少其餘的配置就會被默認進行,如:apache
添加後日志打印以下,能夠發現全部過程只使用了一次數據庫查詢緩存
EBUG [main] - ooo Using Connection [com.mysql.jdbc.JDBC4Connection@5622fdf] DEBUG [main] - ==> Preparing: select user_ID, login_name,user_name, user_code, user_type, user_active, organization_ID,user_position,password from user where user_ID = ? DEBUG [main] - ==> Parameters: 1(Integer) TRACE [main] - <== Columns: user_ID, login_name, user_name, user_code, user_type, user_active, organization_ID, user_position, password TRACE [main] - <== Row: 1, AS-01, 小明, HJ-009, ADMIN, 1, 0, 銷售員, dasfasdfasdfsdf DEBUG [main] - <== Total: 1 =============================================================
能夠在開啓二級緩存時候,手動配置一些屬性安全
<cache eviction="LRU" flushInterval="100000" size="1024" readOnly="true"/>
各個屬性意義以下:併發
能夠在Mapper的具體方法下設置對二級緩存的訪問意願:app
useCache配置
若是一條語句每次都須要最新的數據,就意味着每次都須要從數據庫中查詢數據,能夠把這個屬性設置爲false,如:
<select id="selectAll" resultMap="BaseResultMap" useCache="false">
刷新緩存(就是清空緩存)
二級緩存默認會在insert、update、delete操做後刷新緩存,能夠手動配置不更新緩存,以下:
<update id="updateById" parameterType="User" flushCache="false" />
自定義緩存對象,該對象必須實現 org.apache.ibatis.cache.Cache 接口,以下:
import org.apache.ibatis.cache.Cache; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * Created by Luky on 2017/10/14. */ public class BatisCache implements Cache { private ReadWriteLock lock = new ReentrantReadWriteLock(); private ConcurrentHashMap<Object,Object> cache = new ConcurrentHashMap<Object, Object>(); private String id; public BatisCache(){ System.out.println("初始化-1!"); } //必須有該構造函數 public BatisCache(String id){ System.out.println("初始化-2!"); this.id = id; } // 獲取緩存編號 public String getId() { System.out.println("獲得ID:" + id); return id; } //獲取緩存對象的大小 public int getSize() { System.out.println("獲取緩存大小!"); return 0; } // 保存key值緩存對象 public void putObject(Object key, Object value) { System.out.println("往緩存中添加元素:key=" + key+",value=" + value); cache.put(key,value); } //經過KEY public Object getObject(Object key) { System.out.println("經過kEY獲取值:" + key); System.out.println("OVER"); System.out.println("======================================================="); System.out.println("值爲:" + cache.get(key)); System.out.println("=====================OVER=============================="); return cache.get(key); } // 經過key刪除緩存對象 public Object removeObject(Object key) { System.out.println("移除緩存對象:" + key); return null; } // 清空緩存 public void clear() { System.out.println("清除緩存!"); cache.clear(); } // 獲取緩存的讀寫鎖 public ReadWriteLock getReadWriteLock() { System.out.println("獲取鎖對象!!!"); return lock; } }
在Mapper文件裏配置使用該自定義的緩存對象,如:
<cache type="com.sanyue.utils.BatisCache"/>
測試以下:
public static void main(String[] args) { SqlSessionFactory factory = SqlSessionFactoryUtil.openSqlSession(); // 得到SqlSession對象 SqlSession sqlSession = factory.openSession(); // 得到dao實體 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); // 進行兩次相同的查詢操做 userMapper.selectByPrimaryKey(1); userMapper.selectByPrimaryKey(1); // 注意,當咱們使用二級緩存時候,sqlSession須要使用commit時候纔會生效 sqlSession.commit(); System.out.println("\n\n============================================================="); // 得到一個新的SqlSession 對象 SqlSession sqlSession1 = factory.openSession(); // 進行相同的查詢操做 sqlSession1.getMapper(UserMapper.class).selectByPrimaryKey(1); sqlSession1.commit(); }
日誌輸出以下:
初始化-2! 獲得ID:com.sanyue.dao.UserMapper 獲取鎖對象!!! 經過kEY獲取值:151355725:1423317450:com.sanyue.dao.UserMapper.selectByPrimaryKey:0:2147483647: select user_ID, login_name,user_name, user_code, user_type, user_active, organization_ID,user_position,password from user where user_ID = ? :1 OVER ======================================================= 值爲:null =====================OVER============================== 獲取鎖對象!!! 獲取鎖對象!!! 經過kEY獲取值:151355725:1423317450:com.sanyue.dao.UserMapper.selectByPrimaryKey:0:2147483647: select user_ID, login_name,user_name, user_code, user_type, user_active, organization_ID,user_position,password from user where user_ID = ? :1 OVER ======================================================= 值爲:null =====================OVER============================== 獲取鎖對象!!! 獲取鎖對象!!! 往緩存中添加元素:key=151355725:1423317450:com.sanyue.dao.UserMapper.selectByPrimaryKey:0:2147483647: select user_ID, login_name,user_name, user_code, user_type, user_active, organization_ID,user_position,password from user where user_ID = ? :1,value=[User{userId=1, loginName='AS-01', password='12121212121', userName='小明', userCode='JSD-009', userType='ADMIN', userActive=true, userPosition='銷售員'}] 獲取鎖對象!!! ============================================================= 獲取鎖對象!!! 經過kEY獲取值:151355725:1423317450:com.sanyue.dao.UserMapper.selectByPrimaryKey:0:2147483647: select user_ID, login_name,user_name, user_code, user_type, user_active, organization_ID,user_position,password from user where user_ID = ? :1 OVER ======================================================= 值爲:[User{userId=1, loginName='AS-01', password='12121212121', userName='小明', userCode='JSD-009', userType='ADMIN', userActive=true, userPosition='銷售員'}] =====================OVER============================== 獲取鎖對象!!!
能夠看出,每次查詢數據庫前,MyBatis都會先在緩存中查找是否有該緩存對象。只有當調用了commit() 方法,MyBatis纔會往緩存中寫入數據,數據記錄的鍵爲 數字編號+Mapper名+方法名+SQL語句+參數
格式,值爲返回的對象值。