mybatis的一级缓存是SqlSession级别的缓存,在操作数据库的时候需要先创建SqlSession会话对象,在对象中有一个HashMap用于存储缓存数据,此HashMap是当前会话对象私有的,别的SqlSession会话对象无法访问。
org.apache.ibatis.executor.CachingExecutor#createCacheKey
@Override
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
//简单执行器创建缓存key实现代码复用,因为一级二级缓存都会用此key
//mybatis缓存控制先查找二级缓存(硬盘、redis)、二级缓存没有的情况在查找一级缓存。
//一级缓存绝对是有的 ,但是二缓存可以没有。
return delegate.createCacheKey(ms, parameterObject, rowBounds, boundSql);
}
org.apache.ibatis.executor.BaseExecutor#query()
@Override
public List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
if (closed) {
throw new ExecutorException("Executor was closed.");
}
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List list;
try {
//计数器
queryStack++;
list = resultHandler == null ? (List) localCache.getObject(key) : null;
if (list != null) {
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
if (queryStack == 0) {
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
// issue #601
deferredLoads.clear();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
return list;
}
private List queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List list;
//localCache为PerpetualCache类型 指的就是我们的一级 一级缓存属于本地缓存 存放在内存中 使用map集合存放
//占位符
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;
}
注意:服务器集群的时候,每个sqlSession有自己独立的缓存相互之间不存在共享,所以在服务器集群的时候容易产生数据冲突问题。
select * from user where #{radom}=#{radom}
sqlSession.clearCache();
二级缓存是mapper级别的缓存,也就是同一个namespace的mappe.xml,当多个SqlSession使用同一个Mapper操作数据库的时候,得到的数据会缓存在同一个二级缓存区域,二级缓存默认是没有开启的。
@CacheNamespace(implementation = MybatisRedisCache.class)
public interface OrderMapper {
@Insert("insert order_info values (null,#{orderName},#{orderDes})")
public int addOrder(OrderEntity OrderEntity);
@Select("SELECT * FROM order_info;")
public List findByOrder();
}
// mybatis二级缓存整合Redis
public class MybatisRedisCache implements Cache {
private static Logger logger = LoggerFactory.getLogger(MybatisRedisCache.class);
private Jedis redisClient = createReids();
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private String id;
public MybatisRedisCache(final String id) {
if (id == null) {
throw new IllegalArgumentException("Cache instances require an ID");
}
logger.debug(">>>>>>>>>>>>>>>>>>>>>>>>MybatisRedisCache:id=" + id);
this.id = id;
}
public String getId() {
return this.id;
}
public int getSize() {
return Integer.valueOf(redisClient.dbSize().toString());
}
public void putObject(Object key, Object value) {
logger.debug(">>>>>>>>>>>>>>>>>>>>>>>>putObject:" + key + "=" + value);
redisClient.set(SerializeUtil.serialize(key.toString()), SerializeUtil.serialize(value));
}
public Object getObject(Object key) {
Object value = SerializeUtil.unserialize(redisClient.get(SerializeUtil.serialize(key.toString())));
logger.debug(">>>>>>>>>>>>>>>>>>>>>>>>getObject:" + key + "=" + value);
return value;
}
public Object removeObject(Object key) {
return redisClient.expire(SerializeUtil.serialize(key.toString()), 0);
}
public void clear() {
redisClient.flushDB();
}
public ReadWriteLock getReadWriteLock() {
return readWriteLock;
}
protected static Jedis createReids() {
JedisPool pool = new JedisPool("127.0.0.1", 6379);
return pool.getResource();
}
}
实现Serializable接口
@Data
public class UserEntity implements Serializable {
private Long id;
private String name;
private Integer age;
public UserEntity(Long id, String name, Integer age) {
this.id = id;
this.name = name;
this.age = age;
}
}
public class HelloMybatisTest {
public static void main(String[] args) {
try {
// 1.mybatis配置文件
String resources = "mybatis.xml";
// 2.获取Reader对象
Reader resourceAsReader = Resources.getResourceAsReader(resources);
// 3.获取SqlSessionFactoryBuilder
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsReader);
// 4.创建对应的session
SqlSession sqlSession = build.openSession();
// 5.获取对应的mapper
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 6.执行方法
//UserEntity user = userMapper.getUser(1);
UserEntity user1= sqlSession.selectOne("com.grape.demo.mybatis.hello.mapper.UserMapper.getUser", 1);
//提交之后才会更新到redis里
sqlSession.commit();
SqlSession sqlSession1 = build.openSession();
UserEntity user2= sqlSession1.selectOne("com.grape.demo.mybatis.hello.mapper.UserMapper.getUser", 1);
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
public List query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
Cache cache = ms.getCache();
if (cache != null) {
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, parameterObject, boundSql);
@SuppressWarnings("unchecked")
List list = (List) tcm.getObject(cache, key);
if (list == null) {
//简单执行器查询数据
list = delegate. query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
//放入缓存TransactionalCache里面 list数据放在entriesToAddOnCommit里面
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
return delegate. query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
注意到执行完毕后并没有把缓存更新到redis,此时我们查看org.apache.ibatis.cache.decorators.TransactionalCache#commit方法可知,当我们提交时才会更新到我们缓存里面。
private void flushPendingEntries() {
for (Map.Entry
TransactionalCache:继承自Cache接口,主要作用是保存SqlSession在事务中需要向某个二级缓存提交的缓存数据(因为事务过程中的数据可能会回滚,所以不能直接把数据就提交二级缓存,而是暂存在TransactionalCache中,在事务提交后再将过程中存放在其中的数据提交到二级缓存,如果事务回滚,则将数据清除掉)
TransactionalCacheManager:用于管理CachingExecutor使用的二级缓存对象,只定义了一个transactionalCaches字段
private final Cache delegate; //对应的二级缓存对象
private boolean clearOnCommit; //是否在commit时清除二级缓存的标记
// 需要在commit时提交到二级缓存的数据
private final Map
StatementHandler接口的实现大致有四个,其中三个实现类都是和JDBC中的Statement响对应的:
就是将Statement实例执行之后返回的ResultSet结果集转换成我们需要的List结果集