目录
缓存
MyBatis缓存
1一级缓存
1.1sqlSession相同,查询条件相同
1.2 sqSession相同,查询条件不同
1.3 sqlSession不同,查询条件相同
1.4 sqlSession相同,两次查询之间执行了增删改 *** 作
1.5 sqlSession相同,手动清除一级缓存
1.6 手动配置刷新缓存flushCache
1.7 小结
2二级缓存
2.1使用
2.2实 ***
2.3注意事项
3第三方缓存实现----Ehcache
结语
缓存
介绍:
一个临时的存储空间,可以将用户经常访问的数据存到这个空间当中,当用户下一次访问时,就不需要频繁的与数据库进行交互,而是可以直接从缓存当中拿到数据。
优点:
使用缓存, 我们可以避免频繁的与数据库进行交互, 减少系统开销,提高系统效率。
那么哪些数据适合存到缓存呢?
答:经常查询并且不经常改变的数据。
MyBatis缓存MyBatis提供了对缓存的支持,本章将其分为了以下三种
一级缓存(默认,SqlSession级别的缓存,也称为本地缓存)
二级缓存(需手动开启,namespace级别的缓存)
第三方缓存实现----Ehcache
MyBatis查询数据的顺序是:先去二级缓存中查询,有则返回,无则前往一级缓存查询,如果还没有,再前往数据库进行查询。
接下来一心同学就开始讲解这三种缓存。
1一级缓存作用范围:一个sqlSession的生命周期
功能:同一个 SqlSession 对象, 在参数和 SQL 完全一样的情况下, 只执行一次 SQL 语句,对于其它同样的查询,直接从缓存中调取。
测试:
准备工作:加入日志,方便在运行结果查看。
1.1sqlSession相同,查询条件相同
@Test public void testByID(){ SqlSession sqlSession= MybatisUtils.getSession(); BlogMapper mapper= sqlSession.getMapper(BlogMapper.class); //第一次查询 Blog blog=mapper.getBlogById(1); System.out.println(blog); //第二次查询 Blog blog2=mapper.getBlogById(1); System.out.println(blog2); sqlSession.close(); }
运行结果:
分析:
红框:表示SQL语句只查询了一次。
蓝框:输出结果。
1.2 sqSession相同,查询条件不同@Test public void testByID(){ SqlSession sqlSession= MybatisUtils.getSession(); BlogMapper mapper= sqlSession.getMapper(BlogMapper.class); //第一次查询 Blog blog=mapper.getBlogById(1); System.out.println(blog); //第二次查询 Blog blog2=mapper.getBlogById(2); System.out.println(blog2); sqlSession.close(); }
运行结果:
分析:
红框:表示SQL语句查询了两次次。
蓝框:输出结果。
1.3 sqlSession不同,查询条件相同@Test public void testByID(){ SqlSession sqlSession= MybatisUtils.getSession(); SqlSession sqlSession2=MybatisUtils.getSession(); BlogMapper mapper= sqlSession.getMapper(BlogMapper.class); BlogMapper mapper2= sqlSession2.getMapper(BlogMapper.class); //第一次查询 Blog blog=mapper.getBlogById(1); System.out.println(blog); //第二次查询 Blog blog2=mapper2.getBlogById(1); System.out.println(blog2); sqlSession.close(); }
运行结果:
分析:
红框:表示SQL语句查询了两次。
蓝框:输出结果。
1.4 sqlSession相同,两次查询之间执行了增删改 *** 作@Test public void testByID(){ SqlSession sqlSession= MybatisUtils.getSession(); BlogMapper mapper= sqlSession.getMapper(BlogMapper.class); Mapmap=new HashMap (); map.put("id",1); map.put("pwd","123"); //第一次查询 Blog blog=mapper.getBlogById(1); System.out.println(blog); //进行update *** 作 mapper.updateBlog(map); //第二次查询 Blog blog2=mapper.getBlogById(1); System.out.println(blog2); sqlSession.close(); }
运行结果:
分析:
红框:表示SQL语句查询了两次。
绿框:进行了更新 *** 作
蓝框:输出结果。
1.5 sqlSession相同,手动清除一级缓存@Test public void testByID(){ SqlSession sqlSession= MybatisUtils.getSession(); BlogMapper mapper= sqlSession.getMapper(BlogMapper.class); //第一次查询 Blog blog=mapper.getBlogById(1); System.out.println(blog); //清除一级缓存 sqlSession.clearCache(); //第二次查询 Blog blog2=mapper.getBlogById(1); System.out.println(blog2); sqlSession.close(); }
运行结果:
分析:
红框:表示SQL语句查询了两次。
蓝框:输出结果。
1.6 手动配置刷新缓存flushCache再来看同一个sqlSession查询同样的数据:
@Test public void testByID(){ SqlSession sqlSession= MybatisUtils.getSession(); BlogMapper mapper= sqlSession.getMapper(BlogMapper.class); //第一次查询 Blog blog=mapper.getBlogById(1); System.out.println(blog); //第二次查询 Blog blog2=mapper.getBlogById(1); System.out.println(blog2); sqlSession.close(); }
结果:
分析:
红框:表示SQL语句查询了两次。
蓝框:输出结果。
可知配置flushCache="true"会将执行完之后自动清空缓存。
1.7 小结一级缓存的生命周期:
- MyBatis在开启一个数据库会话时,会创建一个新的SqlSession对象,SqlSession对象中会有一个新的Executor对象,Executor对象中持有一个新的PerpetualCache对象;当会话结束时,SqlSession对象及其内部的Executor对象还有PerpetualCache对象也一并释放掉。
- 在同一个 SqlSession 中, Mybatis 会把执行的方法和参数通过算法生成缓存的键值, 将键值和结果存放在一个 Map 中, 如果后续的键值一样, 则直接从 Map 中获取数据;
- 不同的 SqlSession 之间的缓存是相互隔离的;
- 如果SqlSession调用了close()方法,会释放掉一级缓存PerpetualCache对象,一级缓存将不可用;
- 如果SqlSession调用了clearCache(),会清空PerpetualCache对象中的数据,但是该对象仍可使用;
- SqlSession中执行了任何的 UPDATE, INSERT, DELETE 语句都会清空缓存,但是该对象可以继续使用。
- 任何的 UPDATE, INSERT, DELETE 语句都会清空缓存。
生命周期:存在于 SqlSessionFactory 生命周期中。
介绍:
1.一级缓存的特点是如果sqlSession会话关闭了,这个会话对应的一级缓存就没了,而二级缓存则是sqlSession会话关闭了,一级缓存中的数据被保存到二级缓存中;
2.新的会话查询信息,就可以从二级缓存中获取内容。
3.不同的mapper查出的数据会放在自己对应的缓存(map)中
4.二级缓存是事务性的。这意味着,当 SqlSession 完成并提交时,或是完成并回滚,并且没有执行 flushCache=true 的 insert/delete/update 语句时,二级缓存会获得更新。
注意:使用二级缓存时,sqlSession一定要进行提交或关闭,数据才会存入二级缓存。
System.out.println(mapper1.selectBlogById(1)); // 事务不提交的情况下,二级缓存不会写入 // session1.commit(); System.out.println(mapper2.selectBlogById(1));
2.1使用
1.开启全局缓存(默认情况下是开启的) mybatis.config.xml
2.entity 实现序列化接口
public class Blog implements Serializable { private static final long serialVersionUID = -4852658907724408209L; ... }
3.在 *Mapper.xml 中开启二级缓存, 默认是不开启的。
或
这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。
eviction:
eviction 对应的是回收策略, 默认为 LRU。
LRU: 最近最少使用, 移除最长时间不被使用的对象。
FIFO: 先进先出, 按对象进入缓存的顺序来移除对象。
SOFT: 软引用, 移除基于垃圾回收器状态和软引用规则的对象。
WEAK: 弱引用, 移除基于垃圾回收器状态和弱引用规则的对象。
flushInterval:
对应刷新间隔, 单位毫秒, 默认值不设置, 即没有刷新间隔, 缓存仅仅在刷新语句时刷新。
如果设定了之后, 到了对应时间会过期, 再次查询需要从数据库中取数据。
size:
对应为引用的数量,即最多的缓存对象数据, 默认为 1024。
readOnly:
为只读属性, 默认为 false
false: 可读写, 在创建对象时, 会通过反序列化得到缓存对象的拷贝。 因此在速度上会相对慢一点, 但重在安全。
true: 只读, 只读的缓存会给所有调用者返回缓存对象的相同实例。 因此性能很好, 但如果修改了对象, 有可能会导致程序出问题。
@Test public void testByID(){ SqlSession sqlSession= MybatisUtils.getSession(); SqlSession sqlSession2= MybatisUtils.getSession(); BlogMapper mapper= sqlSession.getMapper(BlogMapper.class); BlogMapper mapper2=sqlSession2.getMapper(BlogMapper.class); //第一次查询 Blog blog=mapper.getBlogById(1); System.out.println(blog); sqlSession.commit(); //第二次查询 Blog blog2=mapper2.getBlogById(1); System.out.println(blog2); sqlSession2.commit(); sqlSession.close(); sqlSession2.close(); }
运行结果:
分析:
红色框:表示SQL语句查询了一次。
蓝色款:输出结果。
用了两个sqlSession,在第二个sqlSession查询中不需要去数据库拿,因为第一个sqlSession已经将其存入到二级缓存当中了。
那么如果在这个过程我们不提交会发生什么呢?
@Test public void testByID(){ SqlSession sqlSession= MybatisUtils.getSession(); SqlSession sqlSession2= MybatisUtils.getSession(); BlogMapper mapper= sqlSession.getMapper(BlogMapper.class); BlogMapper mapper2=sqlSession2.getMapper(BlogMapper.class); //第一次查询 Blog blog=mapper.getBlogById(1); System.out.println(blog); // sqlSession.commit(); //第二次查询 Blog blog2=mapper2.getBlogById(1); System.out.println(blog2); sqlSession2.commit(); sqlSession.close(); sqlSession2.close(); }
运行结果:
分析:
红色框:表示SQL语句查询了两次次。
蓝色款:输出结果。
可知如果sqlSession不提交commit()或者不关闭close(),那么数据就不会存入到二级缓存当中。
2.3注意事项1.由于在更新时会刷新缓存, 因此需要注意使用场合:查询频率很高, 更新频率很低时使用, 即经常使用 select, 相对较少使用delete, insert, update。
2.缓存是以 namespace 为单位的,不同 namespace 下的 *** 作互不影响。但刷新缓存是刷新整个 namespace 的缓存, 也就是你 update 了一个, 则整个缓存都刷新了。
3.最好在 「只有单表 *** 作」 的表的 namespace 使用缓存, 而且对该表的 *** 作都在这个 namespace 中。 否则可能会出现数据不一致的情况。
介绍:第三方实现缓存
1.导入依赖jar包
org.mybatis.caches mybatis-ehcache1.1.0
2.mapper.xml中使用对应的缓存即可
测试:
@Test public void testByID(){ SqlSession sqlSession= MybatisUtils.getSession(); SqlSession sqlSession2= MybatisUtils.getSession(); BlogMapper mapper= sqlSession.getMapper(BlogMapper.class); BlogMapper mapper2=sqlSession2.getMapper(BlogMapper.class); //第一次查询 Blog blog=mapper.getBlogById(1); System.out.println(blog); sqlSession.commit(); //第二次查询 Blog blog2=mapper2.getBlogById(1); System.out.println(blog2); sqlSession2.commit(); sqlSession.close(); sqlSession2.close(); }
运行结果:
由结果可知,这种方式同样可以达到二级缓存的效果。
编写ehcache.xml文件放在resources,如果在加载时未找到/ehcache.xml资源或出现问题,则将使用默认配置。
其中的属性介绍如下:
diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。参数解释如下: user.home – 用户主目录 user.dir – 用户当前工作目录 java.io.tmpdir – 默认临时文件路径
最后,让一心同学扔出一张图,让大家看懂缓存的机制:
结语
以上就是一心同学对MyBatis缓存的讲解,下一个博客我将会讲解用注解开发MyBatis。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)