并发策略

在 Hibernate 中启用二级缓存后,您需要向 Hibernate 说明我们要缓存哪些 Entity 对象以及如何缓存。

为此,Hibernate 为实体类提供了一个特殊的注释 - @Cache。例子:

@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)

需要为我们要为其使用二级缓存的每个 Entity 实体编写此注释。例子:

@Entity
@Table(name = "employee")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Employee {
    @Id
    private Integer id;
    private Set<Task> tasks;
}

如果从不同线程访问缓存实体,Hibernate 有 4 种可能的访问策略:

  • 只读
  • 读写
  • 非严格读写
  • 事务性的

只读。一种永不改变的适合数据的并发策略。Hibernate 将简单地将这些对象存储在它的内存中。仅将其用作参考数据。

数据库存储了很多永远不会改变的信息。例如,一个表保留了一个只添加但从未更改或删除的事件列表。如果您需要通过 Hibernate 使用此表,那么只读缓存策略将适合您。

读写(read-write)。对主要可读的数据使用此策略。然而,Hibernate 将跟踪更改此数据的尝试,尽管它希望它们很少发生。

您需要主要缓存那些很少更改且经常被读取/请求的对象。如果你有这样的对象,那么你需要为它们使用读写策略。

非严格读写。该策略不保证缓存和数据库之间的一致性。如果数据几乎从不更改并且过时数据的小概率不是关键问题,请使用此策略。

与读写策略不同,此策略假定可变数据未锁定以供读取。这可能导致对象在一个地方被更改,而在另一个地方,有人正在阅读它的旧版本。

例如,某个用户更改了他的评论,但其他用户一段时间内仍能看到他的旧版本。如果这对您来说不是问题,则使用非严格读写策略。

事务性的。将此策略主要用于只读数据,在极少数更新情况下防止并发事务中的陈旧数据很重要。

将数据存储在缓存中

您应该记住的关于二级缓存的另一个重要细节是 Hibernate 本身不存储您的类的对象。它将信息存储为字符串、数字等的数组。

对象标识符充当指向此信息的指针。从概念上讲,这有点像 Map,其中对象的 id 是键,数据数组是值。你可以这样想象:

1 -> { "Ivanov", 1, null , {1,2,5} }
2 -> { "Petrov", 2, null , {1,2,5} }
3 -> { "Sidorov", 3, null , {1,2,5} }

考虑到每个对象占用多少额外内存,这是非常合理的。

除了上述之外,您还应该记住,默认情况下您的实体类的依赖项也不会被缓存。例如,如果我们考虑上面的类Employee,那么在获取时,任务集合将从数据库中检索,而不是从二级缓存中检索

如果您还想缓存依赖项,那么该类应该如下所示:

@Entity
@Table(name = "employee")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Employee {
    @Id
    private Integer id;

   @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
   private Set<Task> tasks;
}

最后一个细节——只有在一级缓存中找不到所需对象时,才会从二级缓存中读取。

缓存模式

Hibernate 允许非常灵活的缓存管理。您可以为每个单独的会话甚至每个数据库请求设置缓存模式。

有五种这样的模式:

  • 得到
  • 忽略
  • 普通的
  • 刷新

下表描述了他们的工作:

缓存模式 描述
得到 数据从缓存中读取但不添加到缓存中。
忽略 会话不与缓存交互。
普通的 从缓存中读取数据并将其添加到其中。
数据永远不会从缓存中取出,而是添加到缓存中。
刷新 数据永远不会从缓存中取出,而是添加到缓存中。在这种模式下,额外使用 hibernate.cache.use_minimal_puts 设置。

为会话设置缓存模式的示例:

session.setCacheMode(CacheMode.GET);
Employee director = session.createQuery("from Employee where id = 4").uniqueResult();

还有一个设置会话和请求模式的示例:

session.setCacheMode(CacheMode.GET);
Query query = session.createQuery("from Employee where id = 4");
query.setCacheMode(CacheMode.IGNORE); // Ignore cache work for this request
Employee director = query.uniqueResult();