Hibernate缓存机制

1、缓存范围分类

应用程序中根据缓存的范围,可将缓存分为三类:

(1)事务范围缓存

事务范围缓存,即一级缓存,是单 Session 缓存。其只能被当前事务访问,每个事务都有各自的缓存。缓存的生命周期依赖于事务的生命周期:当事务结束时,缓存的生命周期也会结束。事务范围的缓存使用内存作为存储介质。
Hibernate 中的一级缓存就属于事务范围。

(2)应用范围缓存

应用范围缓存,即二级缓存,是单 SessionFactory 缓存。其可以被应用程序内的所有事务共享访问。缓存的生命周期依赖于应用的生命周期,当应用结束时,缓存的生命周期同时结束。应用范围的缓存可以使用内存或硬盘作为存储介质。
Hibernae 的二级缓存就属于应用范围。

(3)集群范围缓存

集群范围缓存,是多 SessionFactory 缓存。在集群环境中,缓存被一个机器或多个机器的进程共享,缓存中的数据被复制到集群环境中的每个进程节点,进程间通过远程通信来保证缓存中的数据的一致性,缓存中的数据通常采用对象的松散数据形式。
有些 Hibernate 的二级缓存第三方插件支持集群范围缓存

 

2、一级缓存

一级缓存,就是 Session 缓存,其实就是内存中的一块空间,在这个内存空间存放了相互关联的 Java 对象。

Session 缓存是事务级缓存。伴随着事务的开启而开启,伴随着事务的关闭而关闭。Session 缓存由 Hibernate 进行管理。

Session 缓存,是 Hibernate 内置的。是不能被程序员取消的。即,只要使用 Hibernate,就一定要使用,更确切地说,就一定在使用 Session 缓存。

当程序调用 Session 的 load()方法、get()方法、save()方法、saveOrUpdate()方法、update()方法或 Query 接口方法时,Hibernate 会对实体对象进行缓存。

当通过 get()或 load()方法查询实体对象时,Hibernate 会首先到缓存中查询,在找不到实体对象的情况下,Hibernate 才会发出 SQL 语句到 DB 中查询。从而提高了 Hibernate 的使用效率。

 

与一级缓存管理相关的方法如下,它们均为 Session 实例的方法。

  • evict(Object o):从 Session 中删除指定对象
  • clear():无参数,将 Session 缓存清空
  • contains(Object o):判断指定对象是否在 Session 中存在
  • flush():无参数,将 Session 中对象状态同步到 DB 中

 

3、快照

快照,即副本。Hibernate 中的快照,即数据库的副本。快照中的数据由 Hibernate 自己维护。快照中的数据始终保持与 DB 中数据是一致的,不能由代码对副本中内容进行修改。其作用主要是为了在处理数据更新时,将 session 中数据与快照中数据进行对比(即与 DB中数据进行对比,因为快照中数据与 DB 中数据始终是一致的),以此来判断是否真正执行update 语句。

当代码通过 session 的查询方法调用,将数据加载到内存后,框架会将此数据存于 Session缓存,当然快照中也有该数据的副本。session 缓存中数据是可以修改的,但快照中数据是不能修改的,始终保持与 DB 中数据是一致的。默认情况下,当事务在提交时,会首先对比Session 缓存中数据与快照中数据。若不一致,则说明数据发生更新,会将 Session 缓存中数据通过 update 语句更新到 DB 中。当然,快照中数据也会更新。若一致,则说明数据未发生改变,无需做数据同步。

 

Session的刷新与同步

Session 的刷新是指,Session 缓存中数据的更新。Session 的同步是指,将 Session 缓存中的数据同步更新到 DB 中。执行同步的时间点只有一个:事务的提交

当代码中执行了对 Session 中现有数据的修改操作,即 update()与 delete()语句后,Session缓存并不会马上刷新,即并不会马上执行 update 与 delete 的 SQL 语句,而是在某个时间点到来时,才会刷新缓存,更新缓存中数据。刷新时间点主要有三个
(1)执行 Query 查询
(2)执行 session.flush()
(3)执行事务的提交

不过,需要注意,增删改操作,当刷新时间点到来时是否马上进行缓存更新,各自情况还是不同的。

(1)删除操作:对于删除操作,当刷新时间点到来时,会马上执行缓存更新,即马上执行 delete 语句。

(2)修改操作:对于修改操作,当刷新时间点到来时,是否会马上执行缓存更新,即update 语句是否马上执行,还要看修改后的数据是否与快照中数据一致。若一致,即数据实际未被修改,则不执行 update 语句。否则,当到达刷新时间点时,会执行 update 语句。

(3)插入操作:对于插入操作,由于不是修改 Session 中的现有数据,所以与刷新时间点没有关系。在执行完 save()方法后,会马上执行 Insert 语句,更新 Session 缓存中的数据。

当然,刷新过缓存后,即缓存中数据被更新后,是否真正能够同步到数据库,还要看最终事务是否被提交。若最终事务未提交,或发生回滚,则刷新过的缓存内容是无法同步到DB 中的。

 

修改刷新模式

缓存刷新模式,即缓存刷新时间点。当这三个刷新时间点到来时,是否会刷新缓存数据,决定于缓存刷新模式的设置。通过 Session 的 setFlushMode()方法,可以设置缓存刷新模式。

Hibernate 中缓存刷新模式通过 FlushMode 的常量进行指定。其值主要有四种:

缓存刷新模式Query查询事务的提交session.flush()
FlushMode.AUTO(默认)会刷新会刷新会刷新
FlushMode.ALWAYS会刷新会刷新会刷新
FlushMode.COMMIT不会刷新会刷新会刷新
FLushMode.MANUAL不会刷新不会刷新会刷新

 

4、二级缓存

二级缓存是 SessionFactory 级的缓存,其生命周期与 SessionFactory 一致。SessionFactory缓存可以依据功能和目的的不同而划分为内置缓存和外置缓存。

SessionFactory 的内置缓存中存放了映射元数据和预定义 SQL 语句,映射元数据是映射文件中数据的副本,而预定义 SQL 语句是在 Hibernate 初始化阶段根据映射元数据推导出来的 SQL。SessionFactory 的内置缓存是只读的,应用程序不能修改缓存中的映射元数据和预定义 SQL 语句,因此 SessionFactory 不需要进行内置缓存与映射文件的同步。

SessionFactory 的外置缓存是一个可配置的插件。在默认情况下,SessionFactory 不会启用这个插件。外置缓存的数据是数据库数据的副本,外置缓存的介质可以是内存或者硬盘。SessionFactory 的外置缓存也被称为 Hibernate 的二级缓存

Hibernate 本身只提供了二级缓存的规范,但并未实现,故需要第三方缓存产品的支持。

 

Hibernate缓存执行顺序

当 Hibernate 根据 ID 访问数据对象时,首先会从一级缓存 Session 中查找。若查不到且配置了二级缓存,则会从二级缓存中查找;若还查不到,就会查询数据库,把结果按照 ID放入到缓存中。
执行增、删、改操作时,会同步更新缓存。

 

根据缓存内容的不同,可以将 Hibernate 二级缓存分为三类:

(1)类缓存:缓存对象为实体类对象
(2)集合缓存:缓存对象为集合类对象
(3)查询缓存:缓存对象为查询结果

 

二级缓存的并发访问策略

  • 事务型(transactional):隔离级别最高,对于经常被读但很少被改的数据,可以采用此策略。因为它可以防止脏读与不可重复读的并发问题。发生异常的时候,缓存也能够回滚(系统开销大)。
  • 读写型(read-write):对于经常被读但很少被改的数据,可以采用此策略。因为它可以防止脏读的并发问题。更新缓存的时候会锁定缓存中的数据。在 EHCache 中,使用该策略,将无法从二级缓存中获取数据。
  • 非严格读写型(nonstrict-read-write):不保证缓存与数据库中数据的一致性。对于极少被改,并且允许偶尔脏读的数据,可采用此策略。不锁定缓存中的数据。
  • 只读型(read-only):对于从来不会被修改的数据,可使用此策略。

 

类缓存:二级缓存为单独开辟的内存空间,与 Session 缓存不同,所以存放在这两个缓存中的对象也为不同对象。即使它们是同一查询语句查询出的结果。类缓存中存放的是缓存对象的详情

集合缓存集合缓存只缓存集合元素对象的 id,不缓存详情

Query缓存:(1)Query查询结果会存放于缓存中。(2)Query查询不会从缓存中读取数据,(可通过设置改变)。

 

绕过一级缓存的修改

对从 DB 中查询出的对象进行的修改,一般都是通过修改 Session 缓存中的数据后,同步到 DB 中的。但,Query 接口也提供了绕过缓存对数据进行修改的 API:通过 executeUpdate()方法,可以绕过一级缓存直接修改 DB 中的数据。

// 例如
session.createQuery("update Country set cname=? where cid=?")
       .setString(0,"DEF")
       .setInteger(1,1)
       .executeUpdate();

该修改没有绕过二级缓存。而是修改了二级缓存中的一个状态变量 updateTimestamp 修改时间戳。该变量值的修改,将会对查询是否执行 select 语句,即查询结果是否从二级缓存是直接读取,起决定作用:该值被修改,则执行 select 语句,从 DB 中进行查询。否则,直接从二级缓存中读取数据,不再执行 select 语句。

在二级缓存存放的对象,比一级缓存中多出一个属性:updateTimeStamp,修改时间戳。只要这个属性发生改变,就说明有操作修改了 DB 中的数据,二级缓存中的该缓存对象已经不是最新数据,需要从 DB 中再次查询更新。

而 Query 接口的 executeUpdate()方法所进行的更新,可以绕过一级缓存,但会修改二级缓存中缓存对象的 updateTimeStamp 值。其实,只要执行 executeUpdate()方法,都会引发后台进行 update 操作,都会引发 updateTimeStamp 值的改变,都会使二级缓存通过新的查询来更新数据。无论是否真的有数据更新。
 

与二级缓存管理相关的方法

与二级缓存管理相关的方法,一般都定义在 Cache 接口中。而 Cache 对象的获取,需要通过 SessionFactory 的 getCache()方法:Cache cache = sessionFactory.getCache();

部分方法说明如下:

  • evict(Class c):从二级缓存中删除参数指定类型的所有对象。如参数为 Account.class,则会删除二级缓存中所有 Account 类型的对象。
  • evictCollection(String s):从二级缓存中删除指定的集合。注意,该参数要指定对象的集合属性名。如删除 com.abc.Country 实体中的 ministers 集合  evictCollection(“com.abc.Country.ministers”)。
  • evictEtity(String s):与 evict()的作用相同,只不过这里的参数为字符串类型的完整性类名。
  • evictEtity(String s,Serializable id):从二级缓存中删除由 s 指定类型,由 id 指定的对象。

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值