一、什么是mybatis的一级缓存?
当我们执行相同的sql语句时,mybatis会先查询一级缓存.(二级缓存开启时,会先走二级)减少db访问次数,提升效率.它是session级别的缓存,生命周期是一次会话.session关闭,一级缓存就失效了.
二、通过代码,看现象
1.通过相同的session,和不同的sesssion去访问数据库.
/**
* 测试一级缓存需要先关闭二级缓存,localCacheScope设置为SESSION
* @throws IOException
*/
@Test
public void testCache() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session1 = sqlSessionFactory.openSession();
SqlSession session2 = sqlSessionFactory.openSession();
try {
BlogMapper mapper0 = session1.getMapper(BlogMapper.class);
BlogMapper mapper1 = session1.getMapper(BlogMapper.class);
Blog blog = mapper0.selectBlogById(1);
System.out.println(blog);
System.out.println("------------------------------");
System.out.println("第二次查询,相同会话,获取到缓存了吗?");
System.out.println(mapper1.selectBlogById(1));
System.out.println("------------------------------");
System.out.println("第三次查询,不同会话,获取到缓存了吗?");
BlogMapper mapper2 = session2.getMapper(BlogMapper.class);
System.out.println(mapper2.selectBlogById(1));
} finally {
session1.close();
}
}
运行结果
小结:
用同一个session 创建的mapper对象.也就是session1创建的mapper0 和mapper1,mapper1查询的时候没有打印sql语句.也就是走的缓存.而session2创建的mapper2查询打印的sql 语句,也证明一级缓存是session级别.不同的session是不共享的.
2.缓存是是什么时候更新的
我们查询以后,在进行修改,在查询
/**
* 一级缓存失效
* @throws IOException
*/
@Test
public void testCacheInvalid() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession();
try {
BlogMapper mapper = session.getMapper(BlogMapper.class);
System.out.println(mapper.selectBlogById(1));
Blog blog = new Blog();
blog.setBid(1);
blog.setName("after modified 666");
mapper.updateByPrimaryKey(blog);
session.commit();
// 相同会话执行了更新操作,缓存是否被清空?
System.out.println("在[同一个会话]执行更新操作之后,是否命中缓存?");
System.out.println(mapper.selectBlogById(1));
} finally {
session.close();
}
}
执行结果:
在修改以后,又打印了sql,说明更新的时候刷新了缓存.(增删改都会删除缓存)
三、缓存的体系结构
mybatis中有一个cache接口.其中的PerpetualCache是一个基础的实现,其他的都是通过包装这个基础的实现,对其功能增强.(装饰器模式)
PerpetualCache类:
PerpetualCache中存储缓存的结构就是一个hashmap.
各个装饰缓存的功能
一级缓存是session级别,那缓存的这个对象是放到哪里的呢,我们猜想是在session,因为这个可以做到,session关闭,缓存也关闭,我们看源码验证一下.
在默认的session 实现类中,有个2个对象.configuration 是一个全局唯一的对象.缓存对象放在里面不合适.我们看看excutor.
发现在baseExecutor确实有这个缓存对象.
流程图:
四、一级缓存有什么问题?
在分布式环境下.多个session可能会查到过时的数据,比如session1查第一次,缓存了数据,这个时候session 修改了session 查询的数据.但是这个时候session1再去查询.还是查询的缓存中的数据.这个时候叫需要二级缓存.