仓库地址:spring-boot-learning
欢迎star、fork,给做者一些鼓励html
学习SpringBoot集成Mybatis的第二章,了解到Mybatis自带的缓存机制,在部署的时候踩过了一些坑。在此记录和分享一下Mybatis的缓存做用。java
本文章的源码再文章末尾git
MyBatis有一级缓存和二级缓存。记录能够看下这篇博文:github
首先看一下什么是一级缓存,一级缓存是指SqlSession。一级缓存的做用域是一个SqlSession。Mybatis默认开启一级缓存。算法
在同一个SqlSession中,执行相同的查询SQL,第一次会去查询数据库,并写到缓存中;第二次直接从缓存中获取。当执行SQL查询先后发生增删改操做时,则SqlSession的缓存清空。spring
具体能够看这段代码:sql
@Test public void testLocalCacheScope() throws Exception { SqlSession sqlSession1 = factory.openSession(true); SqlSession sqlSession2 = factory.openSession(true); StudentMapper studentMapper = sqlSession1.getMapper(StudentMapper.class); StudentMapper studentMapper2 = sqlSession2.getMapper(StudentMapper.class); System.out.println("studentMapper读取数据: " + studentMapper.getStudentById(1)); System.out.println("studentMapper读取数据: " + studentMapper.getStudentById(1)); System.out.println("studentMapper2更新了" + studentMapper2.updateStudentName("小岑",1) + "个学生的数据"); System.out.println("studentMapper读取数据: " + studentMapper.getStudentById(1)); System.out.println("studentMapper2读取数据: " + studentMapper2.getStudentById(1)); }
开启两个sqlSession数据库
从打印日志能够看出,前面两个说明sqlSession1的会话缓存生效了,第三个对sqlSession2会话执行了更新操做,这时候数据库发生数据变化,sqlSession2被清空。但是在执行第四个查询是,是查询的sqlSession1会话,因为sqlSession1没有被清空,因此仍是查询的缓存的数据,是数据更新以前的,查询的是脏数据,一级缓存sqlSession是不共享的。证实了一级缓存只是在数据库会话内部共享的。json
Mybatis的二级缓存是指mapper映射文件。二级缓存的做用域是同一个namespace下的mapper映射文件内容,多个SqlSession共享,Mybatis须要手动设置二级缓存。缓存
在同一个namespace下的mapper文件中,执行相同的查询SQL,第一次会查询数据库,并写道缓存中;第二次z直接从缓存中获取。当执行SQL查询先后发生增删改操做时,则二级缓存清空。
上面说到二级缓存能够共享多个SqlSession。能够解决不一样SqlSession回话中查询到脏数据的问题了。
首先,Mybatis默认是开启一级缓存的,即同一个SqlSession每次查询都会去缓存中查询,没有数据的话,再去数据库获取数据。可是,整合到SpringBoot中后,一级缓存就会被关闭。为何会出现这种缘由呢,能够看下这篇文章:
好了,如今来建立项目,能够根据前一篇文章来建立项目,在这基础上修改
<dependency> <groupId>org.mybatis.caches</groupId> <artifactId>mybatis-ehcache</artifactId> <version>1.1.0</version> </dependency>
<cache />
加上这个标签,二级缓存就会开启,他的默认属性以下
也能够自定义二级缓存的属性,例如:
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
这个更高级的配置建立了一个 FIFO 缓存,并每隔 60 秒刷新,存数结果对象或列表的 512 个引用,并且返回的对象被认为是只读的,所以在不一样线程中的调用者之间修改它们会 致使冲突。
可用的收回策略有:
默认的是 LRU。
flushInterval(刷新间隔)能够被设置为任意的正整数,并且它们表明一个合理的毫秒 形式的时间段。默认状况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新。
size(引用数目)能够被设置为任意正整数,要记住你缓存的对象数目和你运行环境的 可用内存资源数目。默认值是 1024。
readOnly(只读)属性能够被设置为 true 或 false。只读的缓存会给全部调用者返回缓 存对象的相同实例。所以这些对象不能被修改。这提供了很重要的性能优点。可读写的缓存 会返回缓存对象的拷贝(经过序列化) 。这会慢一些,可是安全,所以默认是 false。
编写Controller接口
/** * 查询全部用户信息 * @return */ @RequestMapping("/getAll") private List<SysUserEntity> getUser() { List<SysUserEntity> userList = sysUserService.queryUserAll(); return userList; } /** * 根据userId查询用户信息 * @return */ @RequestMapping("/getUser") private List<SysUserEntity> getUser(@RequestParam(value = "userId", required = false) Long userId) { List<SysUserEntity> userList = sysUserService.queryUserInfo(userId); return userList; } /** * 更新用户信息 * @param user * @return */ @RequestMapping("/updateUser") private int updateUser(@RequestBody SysUserEntity user) { return sysUserService.updateUserInfo(user); }
经过postman发送接口请求进行测试:
更新用户信息接口发送报文:
{ "userId":5, "email":"12321321", "mobile":"11111111111213" }
经过日志能够看到,第一次发送1接口请求,对数据库进行了查询
能够看到,第二次和第三次查询没有查询数据库的SQL打印,而是去数据库获取数据
此时发送3接口,进行更新操做,在发送1接口,查询改用户的数据
能够看到,当执行数据库更新操做后,再进行查询,此时缓存已经清空,须要从数据库中从新查询获取。
这就演示了SpringBoot整合Mybatis的缓存机制测试。
一、缓存的对象必须实现序列化。由于二级缓存的数据不必定都是存储到内存中,它的存储介质多种多样,因此须要给缓存的对象执行序列化,才能够确保获取无误。
二、Mybatis的二级缓存相比于一级缓存来讲,实现了SqlSession之间的缓存数据的共享,作到namespace级别,粒度更细
三、在分布式环境下,因为默认的MyBatis Cache实现都是基于本地的,分布式环境下必然会出现读取到脏数据,须要使用集中式缓存将MyBatis的Cache接口实现,有必定的开发成本,直接使用Redis、Memcached等分布式缓存可能成本更低,安全性也更高。
不过建议Mybatis的缓存特性再生产环境下进行关闭,单纯做为一个ORM框架使用可能更加合适。
下篇文章计划写SpringBoot整合Mybatis,使用Redis实现缓存基本配置。