Mybatis一级缓存和二级缓存的产生和销毁

一、MyBatis一级缓存

1.MyBatis一级缓存默认是开启的

2.流程:

3.MyBatis命中缓存原则

1.StatmentId必须一致,否则无法命中


2.传递给SQL的参数必须一致,比如,传入的map中有两个参数,只要传入的SQL中的id值是一样的,其他参数是什么没影响

3.分页参数必须相同,缓存粒度的是整个分页结果集,而不是结果集中的对象

RowBounds rowBounds1 = new RowBounds(0, 1);
List<TempEntity> tempEntity1 = sqlSession.selectList("com.mybatis.TempDao.list", null, rowBounds1);
logger.info(tempEntity1);
RowBounds rowBounds2 = new RowBounds(0, 2);
List<TempEntity> tempEntity2 = sqlSession.selectList("com.mybatis.TempDao.list", null, rowBounds2);
logger.info(tempEntity2);
logger.info(tempEntity1 == tempEntity2);


分页参数不一致

4.要求传递给JDBC的SQL语句必须是一致的

<select id="getById3" parameterType="java.util.Map" resultType="com.mybatis.TempEntity">
<if test="type==1">
select * from temp where id = #{id}
</if>
<if test="type==2">
select * from temp where 1=1 and id = #{id}
</if>
</select>


虽然,id一致,返回结果一致,但是就是多了where 1=1 ,所以无法命中缓存


5.执行环境必须一致

<environment id="dev">
<!--指定事务管理的类型,这里简单使用Java的JDBC的提交和回滚设置-->
<transactionManager type="JDBC"></transactionManager>
<!--dataSource 指连接源配置,POOLED是JDBC连接对象的数据源连接池的实现-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/mytest"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</dataSource>
</environment>


二、一级缓存的生命周期,什么时候产生什么时候销毁

1.什么时候产生:SqlSession中的select、selectOne、selectList、selectMap执行时会产生缓存


2.什么时候销毁


1.sqlSession.close();

2.sqlSession.commit();

3.sqlSession.rollback();

4.sqlSession.clearCache();

5.执行insert、update、delete之后会清空一级缓存,而且即使查询的先查询的表和后更新的表不是一个表也会清空缓存,清空所有session1一级缓存


3.MyBatis一级缓存是否会产生脏读?并不会

由于关闭sqlSession、执行commit、rollback都会清空一级缓存,所以实际上MyBatis一级缓存的生命周期是在数据库事务的生命周期之内。所以不会产生脏读。


三、源码

一级缓存存在哪?

BaseExecutor中这段代码list = resultHandler == null ? (List)this.localCache.getObject(key) : null;

可以找出存在PerpetualCache类的 localCache实例对象中


public class PerpetualCache implements Cache {
private final String id;
private Map<Object, Object> cache = new HashMap();
public void putObject(Object key, Object value) {
this.cache.put(key, value);
}
public Object getObject(Object key) {
return this.cache.get(key);
}
CachingExecutor: CacheKey key = this.createCacheKey(ms, parameterObject, rowBounds, boundSql);//通过sql,参数生成缓存的key,和一级缓存命中原则有关
BaseExecutor: list = resultHandler == null ? (List)this.localCache.getObject(key) : null;
if (list != null) {
this.handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);//如果缓存中有就处理结果
} else {
list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);//如果缓存没有查数据库
}

MyBatis与Spring集成


未开启事务

每次请求,Spring都会关闭旧的session在创建新的session,所以此时一级缓存无效。

开启事务


在一个事务内,Spring通过ThreadLocal始终使用同一个session,所以此时一级缓存在事务内有效。


四、二级缓存

1. 开启二级缓存:

1.mybatis.xml->configuration标签下->

<settings>
<setting name="cacheEnabled" value="true"/>
</settings>


2.mapper.xml加上<cache/>标签


3.类需要序列化


2.二级缓存和一级缓存的区别

一级缓存:每个session查询自己的cache,cache没有数据再查数据库


二级缓存

二级缓存:session1和session2共用一个cache,如果公共cache查不到,就去查数据库


3.二级缓存的命中原则


在同一个SqlSessionFactory中命中原则和一级缓存一样


4.二级缓存什么时候产生,什么时候销毁


产生条件:1.满足一级缓存产生条件


2.sqlSession执行了close或commit


一级缓存和二级缓存联合工作的原理

二级缓存的销毁:


一级缓存销毁是销毁同一个sqlSession中的缓存,二级缓存是销毁同一个mapper映射文件的缓存

二级缓存只有执行update操作才会销毁。

如果不想销毁加flushCache标签


<update id="updateById" parameterType="int" flushCache="false">


</update>


5.二级缓存的源码


CachingExecutor中:


public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
Cache cache = ms.getCache();//一个个开启了二级缓存的mapper.xml文件所对应的缓存对象
if (cache != null) {
this.flushCacheIfRequired(ms);//二级缓存有数据,判断是否开启flushCache
if (ms.isUseCache() && resultHandler == null) {//当前Statement是否开启二级缓存
this.ensureNoOutParams(ms, boundSql);//没有输出参数
List<E> list = (List)this.tcm.getObject(cache, key);//查二级缓存
if (list == null) {//如果二级缓存没有,调代理对象跟一级缓存一样
list = this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
this.tcm.putObject(cache, key, list);//将查询结果放到二级缓存
}
return list;
}
}
return this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}


数据结构:


CachingExecutor:private final TransactionalCacheManager tcm = new TransactionalCacheManager();

TransactionalCacheManager:private final Map<Cache, TransactionalCache> transactionalCaches = new HashMap();

TransactionalCache:private final Map<Object, Object> entriesToAddOnCommit;


entriesToAddOnCommit:当执行commit时,即将要放到二级缓存的对象,当执行rollback时,就会清空即将要放入二级缓存的对象


执行update时,清空二级缓存


public void clear() {
this.clearOnCommit = true;
this.entriesToAddOnCommit.clear();
}
public void commit() {
if (this.clearOnCommit) {
this.delegate.clear();
}
this.flushPendingEntries();
this.reset();
}

qrcode