問題
1、學習測試時發現了一級緩存並沒有生效,先看案例:
setting配置:
<settings>
<!-- (1) 提供可控制Mybatis框架運行行為的屬性信息 -->
<setting name="lazyLoadingEnabled" value="false" />
<setting name="logImpl" value="STDOUT_LOGGING" />
<setting name="localCacheScope" value="SESSION"/>
</settings>
測試代碼:
@Test
public void test1(){
try(SqlSession sqlSession = sessionFactory.openSession();){
GoodsMapper goodsMapper = sqlSession.getMapper(GoodsMapper.class);
Goods goods = goodsMapper.getGoodsById(1,"電腦2");
Goods goods2 = goodsMapper.getGoodsById(1,"電腦2");
}
}
@Autowired
private GoodsMapper goodsMapper;
@Test
public void test2(){
Goods goods = goodsMapper.getGoodsById(1,"電腦2");
Goods goods2 = goodsMapper.getGoodsById(1,"電腦2");
}
問題分析
首先我們都知道一級緩存的作用范圍有兩種,一種是sqlSession,一種是STATEMENT。我們看test2方法,用的都是同一個mapper,按理說是應該只查詢一次的啊,難道同一個mapper的sqlSession不一樣。緊接着我試了test1方法,用的也都是同一個mapper,發現只查詢了一次數據庫。難道這兩種方法有不一樣的地方嗎?
答案就在MapperProxy類當中,我們打斷點執行test1方法,我們發現他的sqlSession類型是DefaultSqlSession
然后再執行test2方法,我們發現他的sqlSession類型是SqlSessionTemplate
他們的差別找到了,失效的原因多半就是SqlSessionTemplate造成的,SqlSessionTemplate類里有一個SqlSessionInterceptor,我們看他的invoke方法,代碼如下:
private class SqlSessionInterceptor implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
SqlSession sqlSession = getSqlSession(
SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType,
SqlSessionTemplate.this.exceptionTranslator);
try {
Object result = method.invoke(sqlSession, args);
if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
// force commit even on non-dirty sessions because some databases require
// a commit/rollback before calling close()
sqlSession.commit(true);
}
return result;
} catch (Throwable t) {
Throwable unwrapped = unwrapThrowable(t);
if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
// release the connection to avoid a deadlock if the translator is no loaded. See issue #22
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
sqlSession = null;
Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
if (translated != null) {
unwrapped = translated;
}
}
throw unwrapped;
} finally {
if (sqlSession != null) {
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
}
}
此時我們發現sqlSession是通過invoke方法里的getSqlSession方法重新賦值了,而我打斷點發現,同一個mapper調同樣的方法獲得的sqlSession是不一樣的,既然不一樣,那當然讀不到緩存了。
而DefaultSqlSession則是直接執行查詢方法,是同一個sqlSession。所以讀到了緩存。