1. 一级缓存概念
-
什么是一级缓存:一级缓存也叫本地缓存,它是存在于一次SqlSession会话中。
-
为什么需要缓存:缓存的提出简单来说就是为了降低与数据库的频繁交互,从而减轻服务器的压力。
-
缓存的意义:在对数据库的一次SqlSession会话中,我们有可能会反复地执行完全相同的查询语句,为了防止短时间内频繁的访问数据库并且查询同一条语句,而查询一次的资源消耗很大。因此如果不采取一些必要的措施将会造成很大的资源浪费。
为了减少资源浪费问题,mybatis在SqlSession对象中建立了一个一级缓存(本地缓存),每次访问数据库之前先来缓存找,如果有之前完全一样的查询直接从缓存中取结果返回,如果本地缓存中找不到再与数据库进行交互。
2. 一级缓存原理
2.1、缓存的诞生过程
从SqlSessionFactory工厂中获得一个SqlSession会话对象时,SqlSession会创建一个Executor执行器,用于执行SQL语句。同时Executor会 代理 很多东西,事务、缓存、配置… 这里只讨论缓存,同时Executor会创建一个LocalCache。
注意:Executor是一个统称,Executor是一个大类,其中由很多执行器组成。
2.2、缓存的内部实现
package org.apache.ibatis.cache.impl;
import java.util.HashMap;
import java.util.Map;
import org.apache.ibatis.cache.Cache;
import org.apache.ibatis.cache.CacheException;
/**
* @author Clinton Begin 这是mybatis的作者
*/
public class PerpetualCache implements Cache {
private final String id;
private Map<Object, Object> cache = new HashMap<>();
@Override
public int getSize() {
return cache.size();
}
@Override
public void putObject(Object key, Object value) {
cache.put(key, value);
}
@Override
public Object getObject(Object key) {
return cache.get(key);
}
@Override
public Object removeObject(Object key) {
return cache.remove(key);
}
@Override
public void clear() {
cache.clear();
}
}
Cache接口采用PerpetualCache实现类,而PerpetualCache内部真正的核心就是一个HashMap。既然是Key - Value键值对,Value毫无疑问肯定是查询到的结果,Key是针对SQL语句经过特殊运算的一串hashCode
3. 一级缓存的生命周期
3.1、缓存失效
这只是一级缓存大概的生命周期,不完全遵循这个原则。一级缓存大概受到4类操作的影响,会导致缓存更新、失效等问题。
-
SqlSession.close()关闭会话直接导致所有的缓存释放
-
Sqlsession.clearCache()清除一级缓存,释放所有的缓存
-
映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
4. 一级缓存工作原理
4.1、本地缓存中存在
SqlSession传入SQL语句给Executro执行器,执行器进行hashCode运算。得到一个hashCode值,将hashCode值传给LocalCache缓存,缓存直接getValue(hashCode)获取查询结果
4.2、本地缓存中没有(包括各种失效)
只是多了一步将查询结果存入LocalCache的过程。
5. 简单测试
5.1、重复查询同一条语句
@Test
public void test1(){
SqlSession Sqlsession = mybatisUtil.getSqlSession(true);
UserMapper mapper = Sqlsession.getMapper(UserMapper.class);
User user1 = mapper.getUserById(1);
System.out.println(user1.toString());
System.out.println("============================================");
User user2 = mapper.getUserById(1);
System.out.println(user2.toString());
System.out.println("user1 == user2 ? " + (user1==user2));
Sqlsession.close();
}
5.2、手动清除缓存
@Test
public void test2(){
SqlSession Sqlsession = mybatisUtil.getSqlSession(true);
UserMapper mapper = Sqlsession.getMapper(UserMapper.class);
User user1 = mapper.getUserById(1);
System.out.println(user1.toString());
Sqlsession.clearCache(); //清除缓存
System.out.println("============================================");
User user2 = mapper.getUserById(1);
System.out.println(user2.toString());
Sqlsession.close();
}
5.3、insert、update、delete会刷新缓存
进行增删改操作后会清除缓存信息,这样做的原因是因为需要保持数据的一致性。防止出现脏读、幻读、不可重复读。哪怕是这三种操作对缓存中的一些数据不会产生影响都会重新读取。
@Test
public void test3(){
SqlSession Sqlsession = mybatisUtil.getSqlSession(true);
UserMapper mapper = Sqlsession.getMapper(UserMapper.class);
User user1 = mapper.getUserById(1);
System.out.println(user1.toString());
mapper.updUser("123456",2); //更改用户 2
System.out.println("============================================");
User user2 = mapper.getUserById(1);
System.out.println(user2.toString());
System.out.println("user1 == user2 ? " + (user1==user2));
Sqlsession.close();
}
5.4、不同的SqlSession查询
@Test
public void test4(){
SqlSession Sqlsession = mybatisUtil.getSqlSession(true);
UserMapper mapper = Sqlsession.getMapper(UserMapper.class);
User user1 = mapper.getUserById(1);
System.out.println(user1.toString());
Sqlsession.close();
System.out.println("============================================");
Sqlsession = mybatisUtil.getSqlSession(true);
mapper = Sqlsession.getMapper(UserMapper.class);
User user2 = mapper.getUserById(1);
System.out.println(user2.toString());
System.out.println("user1 == user2 ? " + (user1==user2));
Sqlsession.close();
}