MyBatis-缓存

MyBatis-缓存,第1张

MyBatis-缓存

目录

缓存

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);

 Map map=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 小结

一级缓存的生命周期:

  1. MyBatis在开启一个数据库会话时,会创建一个新的SqlSession对象,SqlSession对象中会有一个新的Executor对象,Executor对象中持有一个新的PerpetualCache对象;当会话结束时,SqlSession对象及其内部的Executor对象还有PerpetualCache对象也一并释放掉。
  2. 在同一个 SqlSession 中, Mybatis 会把执行的方法和参数通过算法生成缓存的键值, 将键值和结果存放在一个 Map 中, 如果后续的键值一样, 则直接从 Map 中获取数据;
  3. 不同的 SqlSession 之间的缓存是相互隔离的;
  4. 如果SqlSession调用了close()方法,会释放掉一级缓存PerpetualCache对象,一级缓存将不可用;
  5. 如果SqlSession调用了clearCache(),会清空PerpetualCache对象中的数据,但是该对象仍可使用;
  6. SqlSession中执行了任何的 UPDATE, INSERT, DELETE 语句都会清空缓存,但是该对象可以继续使用。
  7. 任何的 UPDATE, INSERT, DELETE 语句都会清空缓存。

2二级缓存

生命周期:存在于 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: 只读, 只读的缓存会给所有调用者返回缓存对象的相同实例。 因此性能很好, 但如果修改了对象, 有可能会导致程序出问题。
 

2.2实 ***
@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 中。 否则可能会出现数据不一致的情况。
 

3第三方缓存实现----Ehcache

介绍:第三方实现缓存

1.导入依赖jar包


 org.mybatis.caches
 mybatis-ehcache
 1.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 – 默认临时文件路径
属性描述defaultCache默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略。只能定义一个。
 name缓存名称。maxElementsInMemory缓存最大数目maxElementsOnDisk硬盘最大缓存个数。eternal对象是否永久有效,一但设置了,timeout将不起作用。overflowToDisk是否保存到磁盘,当系统当机时timeToIdleSeconds设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。timeToLiveSeconds设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。diskPersistent是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.diskSpoolBufferSizeMB这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。diskExpiryThreadIntervalSeconds磁盘失效线程运行时间间隔,默认是120秒。memoryStoreEvictionPolicy当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。clearOnFlush内存数量最大时是否清除。memoryStoreEvictionPolicy可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。FIFO先进先出。LFU一直以来最少被使用的,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。LRU最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。

最后,让一心同学扔出一张图,让大家看懂缓存的机制:



结语

以上就是一心同学对MyBatis缓存的讲解,下一个博客我将会讲解用注解开发MyBatis。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5637025.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-16
下一篇 2022-12-16

发表评论

登录后才能评论

评论列表(0条)

保存