Mybatis有两级缓存,一级缓存是在同一个SqlSession中有效,两次彻底相同的查询,第二次就是从本地缓存中获得上一次的查询结果java
Mybatis是默认开启一级缓存的,没法关闭。好比在一个会话中连续两次执行同一个方法, 获得的是同一个对象sql
一级缓存在一下六种状况会失效:
1.不一样的会话
2.同一个会话,两次相同查询中间有增删改操做,由于都会调用clearLocalCache()方法清除缓存
3.同一个会话,查询(条件)参数不一样
4.同一个会话,调用清楚会话内容操做 SqlSession.flushCache()
5.同一个会话,SQL相同与条件(参数)相同,可是方法名不一样
6.同一个会话,分页信息(偏移量和须要返回的记录数)不一样数据库
举例:缓存
public void testCache1() { SqlSession session = getSqlSession(); CustMapper mapper = session.getMapper(CustMapper.class); //SQL:select cust_id,cust_name from cust where cust_id=#{custId}; Cust cust1 = mapper.getCustById(1); //SQL:select cust_id,cust_name from cust where cust_id=#{custId}; //虽然getCustById和getCustByIdNew使用的相同条件和相同SQL,但方法不一样不能使用缓存 Cust cust2 = mapper.getCustByIdNew(1); //若是在这里执行一个增删改操做,那么本地缓存中的 数据将会被清空,下面的getCustByIdNew(1)会再次从数据库中查询数据 //mapper.addCust(new Cust("TonTan")) //由于和Cust cust2 = mapper.getCustByIdNew(1);方法名(全类名+方法名)、条件、SQL、默认分页信息都同样因此可使用缓存 Cust cust3 = mapper.getCustByIdNew(1); }
附上一些源码分析:session
一级缓存是使用了名为PerpetualCache的类,变量名为localCache,其内部就是一个Map<Object,Object>。app
public class PerpetualCache implements Cache { private final String id; private Map<Object, Object> cache = new HashMap<Object, Object>(); }
Mybatis每次执行查询的时候会执行localCache.get(key),这个Key是CacheKey类,他的hashCode值是由以下几个因素决定,只要这几个因素彻底一致就能够从localCache中找到以前的查询结果。生成key的方法以下:源码分析
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) { if (closed) { throw new ExecutorException("Executor was closed."); } CacheKey cacheKey = new CacheKey(); cacheKey.update(ms.getId()); cacheKey.update(rowBounds.getOffset()); cacheKey.update(rowBounds.getLimit()); cacheKey.update(boundSql.getSql()); //其余部分没有复制出来 }
ms.getId是方法的全类名+方法名
rowBounds.getOffset()分页查询时的偏移量
rowBounds.getLimit()分页查询时的查询记录数
boundSql.getSql()sql自己code
何时将查询结果放入缓存的喃?对象
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { List<E> list; localCache.putObject(key, EXECUTION_PLACEHOLDER); try { list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql); } finally { localCache.removeObject(key); } localCache.putObject(key, list); if (ms.getStatementType() == StatementType.CALLABLE) { localOutputParameterCache.putObject(key, parameter); } return list; }
localCache.putObject(key, list);就是它了。。。。。。。rem
那为何调用了增删改缓存就失效了喃,由于他们都底层都是调用update方法,而它调用了clearLocalCache(),因此。。你懂的。
最后提一个全局配置参数:localCacheScope,它能够设置为SESSION或者STATEMENT,默认为SESSION,可是这样的话,可能取到脏数据,假如设置为STATEMENT的话就能够避免这种状况,至关于禁止了一级缓存。