一级缓存
1.首先明确什么是sqlSession
sqlSession是Mybatis的关键对象,是执行持久化操作的独享,有点类似于JDBC中的Connection对象,它是应用程序与持久层交互的一个单线程对象,它包括了以数据库为背景的所有执行SQL的操作方法,它的底层封装了JDBC连接,可以用sqlSession实例直接执行被映射的sql语句,每个线程都应该有自己的sqlSession实例,sqlSession实例不能被共享,同时也是线程不安全的,在使用完成之后我们要确保在finally代码块中关闭它。
//SqlSession接口源码如下所示:
package org.apache.ibatis.session;
import java.io.Closeable;
import java.sql.Connection;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.executor.BatchResult;
public interface SqlSession extends Closeable {
<T> T selectOne(String statement);
<T> T selectOne(String statement, Object parameter);
<E> List<E> selectList(String statement);
<E> List<E> selectList(String statement, Object parameter);
<E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds);
<K, V> Map<K, V> selectMap(String statement, String mapKey);
<K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey);
<K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds);
void select(String statement, Object parameter, ResultHandler handler);
void select(String statement, ResultHandler handler);
void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler);
int insert(String statement);
int insert(String statement, Object parameter);
int update(String statement);
int update(String statement, Object parameter);
int delete(String statement);
int delete(String statement, Object parameter);
void commit();
void commit(boolean force);
void rollback();
void rollback(boolean force);
List<BatchResult> flushStatements();
void close();
void clearCache();
Configuration getConfiguration();
<T> T getMapper(Class<T> type);
Connection getConnection();
}
2.说明
- 在同一个sqlSession中,mybatis会把执行的方法名称跟参数按照一定规则拼接成Map的key值,将缓存的结果放入Map的value中,如果后续的key一样则直接读取value。
- 不同的sqlSession中缓存是隔离的
- 使用INSERT,DELETE,UPDATE都会清空缓存
- 使用配置可以清空同一个sqlSession中的缓存
<select id="selectByPrimaryKey" flushCache="true" parameterType="java.lang.Integer" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from student
where student_id=#{id, jdbcType=INTEGER}
</select>
二级缓存
1.开启二级缓存后流程图
如果要实现多个sqlSession共享缓存,则需要开启二级缓存 (使用同一个namespace),sqlSession对象会使用CachingExecutor装饰 Executor 查询相应namespace下的缓存数据,如果没有的话将任务给Executor去查询一级缓存,最后执行查询数据库的过程。
顺序为:二级缓存 -> 一级缓存 -> 数据库
2.二级缓存如何开启
-------------------spring + springMVC + mybatis ----------------------------
<settings>
<setting name = "cacheEnabled" value = "true" />
</settings>
-------------------springBoot+ mybatis --------------------------------------
mybatis-plus.configuration.cache-enabled=true
--------------------- 在mapper.xml文件中使用 ----------------------------------
// 只需要使用cache标签即可 可以自定义缓存类型结合第三方缓存 比如redis
<cache type="com.example.demo.redis.cache.MybatisRedisCache"/>
<cache/>
public class MybatisRedisCache implements Cache {
private static final Logger log = LoggerFactory.getLogger(MybatisRedisCache.class);
private String id;
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
//private static final long EXPIRE_TIME_IN_MINUTES = 30; // redis过期时间
public MybatisRedisCache(String id) {
this.id = id;
}
private RedisTemplate<Object, Object> getRedisTemplate(){
return ApplicationContextHolder.getBean("redisTemplate");
}
@Override
public String getId() {
return id;
}
@Override
public void putObject(Object key, Object value) {
RedisTemplate redisTemplate = getRedisTemplate();
redisTemplate.boundHashOps(getId()).put(key, value);
log.info("[结果放入到缓存中: " + key + "=" + value+" ]");
}
@Override
public Object getObject(Object key) {
RedisTemplate redisTemplate = getRedisTemplate();
Object value = redisTemplate.boundHashOps(getId()).get(key);
log.info("[从缓存中获取了: " + key + "=" + value+" ]");
return value;
}
@Override
public Object removeObject(Object key) {
RedisTemplate redisTemplate = getRedisTemplate();
Object value = redisTemplate.boundHashOps(getId()).delete(key);
log.info("[从缓存删除了: " + key + "=" + value+" ]");
return value;
}
@Override
public void clear() {
RedisTemplate redisTemplate = getRedisTemplate();
redisTemplate.delete(getId());
log.info("清空缓存!!!");
}
@Override
public int getSize() {
RedisTemplate redisTemplate = getRedisTemplate();
Long size = redisTemplate.boundHashOps(getId()).size();
return size == null ? 0 : size.intValue();
}
@Override
public ReadWriteLock getReadWriteLock() {
return readWriteLock;
}
}
//需要注意的点
- 自定义实现的二级缓存,必须要有一个带id的构造函数,否则会报错。
- 这里不能通过@Autowire的方式引用redisTemplate,因为RedisCache并不是Spring容器里的bean。所以我们需要手动地去调用容器的getBean方法来拿到这个bean,那么这样,我们就需要引入ApplicationContextHolder这个类。
3.Mybatis cache标签
<cache/>
- 映射语句中所有select语句都会被缓存
- 映射语句中所有INSERT,UPDATE,DELETE语句都会刷新缓存
- 缓存会使用 Least Recently Used(LRU,最近最少使用的)算法来收回
- 根据时间表(比如 no Flush Interval,没有刷新间隔), 缓存不会以任何时间顺序 来刷新。
- 存会存储列表集合或对象(无论查询方法返回什么)的 1024 个引用。
- 缓存会被视为是 read/write(可读/可写)的缓存,意味着对象检索不是共享的,而 且可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
1. eviction:回收策略
LRU:最近最少被使用的数据
FIFO:先进先出
SOFT:软引用,移除基于垃圾回收器状态和软引用规则的对象
WEAK:弱引用 更积极地移除基于垃圾收集器状态和弱引用规则的对象。
2.flushInterval:定时刷新 单位为毫秒,默认情况不设置 采用语句刷新的方式
3.size:存数结果对象或列表的 512 个引用,默认为 1024.
4.readOnly:而且返回的对象被认为是只读的,因此在不同线程中的调用者之间修改它们会 导致冲突。默认为false,可读写的缓存会返回对象反序列化的拷贝,线程安全但是速度较慢。
-------------------------也可以在特定的sql上利用flushCache,useCache来控制是否开启缓存--------------------
<select ... flushCache="false" useCache="true"/>