Hibernate 缓存
缓存是一种提高系统性能的机制。 它是位于应用程序和数据库之间的缓冲存储器。 高速缓存存储最近使用的数据项,以尽可能减少数据库命中的次数。
缓存对 Hibernate 也很重要。 它使用多级缓存方案,如下所述
First-level Cache
一级缓存是Session缓存,是所有请求都必须通过的强制缓存。 Session 对象在将对象提交到数据库之前将其保持在自己的权力之下。
如果我们对一个对象发出多个更新,Hibernate 会尝试尽可能长时间地延迟执行更新,以减少发出的更新 SQL 语句的数量。 如果我们关闭会话,所有正在缓存的对象都将丢失,并在数据库中保留或更新。
Second-level Cache
二级缓存是可选的缓存,在尝试定位二级缓存中的对象之前,将始终参考一级缓存。 二级缓存可以基于每个类和每个集合进行配置,主要负责跨会话缓存对象。
任何第三方缓存都可以与 Hibernate 一起使用。 提供了一个 org.hibernate.cache.CacheProvider
接口,必须实现该接口才能为 Hibernate 提供缓存实现的句柄。
Query-level Cache
Hibernate 还实现了与二级缓存紧密集成的查询结果集缓存。
这是一个可选功能,需要两个额外的物理缓存区域来保存缓存的查询结果和上次更新表时的时间戳。 这仅对使用相同参数频繁运行的查询有用。
二级缓存
Hibernate 默认使用一级缓存,不用做一级缓存。 让我们直接进入可选的二级缓存。 并非所有类都能从缓存中受益,因此能够禁用二级缓存很重要。
Hibernate 二级缓存分两步设置。 首先,我们必须决定使用哪种并发策略。 之后,我们可以使用缓存提供程序配置缓存过期和物理缓存属性。
并发策略
并发策略是一个中介,它负责将数据项存储在缓存中并从缓存中检索它们。如果要启用二级缓存,则必须为每个持久类和集合决定使用哪种缓存并发策略。
- Transactional - 将此策略用于以读取为主的数据,在这种情况下,在极少数更新的情况下,防止并发事务中的陈旧数据至关重要。
- Read-write - 再次将此策略用于以读取为主的数据,在这种情况下,在极少数更新的情况下,防止并发事务中的陈旧数据至关重要。
- Nonstrict-read-write - 此策略无法保证缓存和数据库之间的一致性。如果数据几乎从未更改并且过时数据的可能性很小不是关键问题,则使用此策略。
- Read-only - 适用于数据的并发策略,永远不会改变。仅将其用作参考数据。
如果我们要为 Employee 类使用二级缓存,让我们添加所需的映射元素,告诉 Hibernate 使用读写策略缓存 Employee 实例。
<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name = "Employee" table = "employee">
<meta attribute = "class-description">
This class contains the employee detail.
</meta>
<cache usage = "read-write"/>
<id name = "id" type = "int" column = "id">
<generator class="native"/>
</id>
<property name = "firstName" column = "first_name" type = "string"/>
<property name = "lastName" column = "last_name" type = "string"/>
<property name = "salary" column = "salary" type = "int"/>
</class>
</hibernate-mapping>
usage="read-write"
属性告诉 Hibernate 对定义的缓存使用读写并发策略。
缓存提供程序
在考虑了并发策略之后的下一步,我们将使用缓存候选类来选择缓存提供程序。 Hibernate 强制我们为整个应用程序选择单个缓存提供程序。
序号 | 缓存名称 | 描述 |
---|---|---|
1 | EHCache | 它可以缓存在内存或磁盘和集群缓存中,并且支持可选的 Hibernate 查询结果缓存。 |
2 | OSCache | 通过一组丰富的过期策略和查询缓存支持,支持在单个 JVM 中缓存到内存和磁盘。 |
3 | warmCache | 基于 JGroups 的集群缓存。 它使用集群失效,但不支持 Hibernate 查询缓存。 |
4 | JBoss Cache | 一个完全事务复制的集群缓存也基于 JGroups 多播库。 它支持复制或失效、同步或异步通信以及乐观和悲观锁。 支持 Hibernate 查询缓存。 |
每个缓存提供程序都不兼容每个并发策略。 以下兼容性矩阵将帮助我们选择合适的组合。
策略/提供程序 | Read-only | Nonstrictread-write | Read-write | Transactional |
---|---|---|---|---|
EHCache | X | X | X | |
OSCache | X | X | X | |
SwarmCache | X | X | ||
JBoss Cache | X | X |
我们将在 hibernate.cfg.xml
配置文件中指定一个缓存提供程序。 我们选择 EHCache 作为我们的二级缓存提供者
<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE hibernate-configuration SYSTEM
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name = "hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</property>
<property name = "hibernate.connection.driver_class">
com.mysql.jdbc.Driver
</property>
<!-- Assume students is the database name -->
<property name = "hibernate.connection.url">
jdbc:mysql://localhost/hibernate_demo
</property>
<property name = "hibernate.connection.username">
root
</property>
<property name = "hibernate.connection.password">
123456
</property>
<property name = "hibernate.cache.provider_class">
org.hibernate.cache.EhCacheProvider
</property>
<!-- List of XML mapping files -->
<mapping resource = "Employee.hbm.xml"/>
</session-factory>
</hibernate-configuration>
现在,我们需要指定缓存区域的属性。 EHCache 有自己的配置文件ehcache.xml,它应该在应用程序的CLASSPATH 中。 Employee 类的 ehcache.xml 中的缓存配置可能如下所示
<diskStore path="java.io.tmpdir"/>
<defaultCache
maxElementsInMemory = "1000"
eternal = "false"
timeToIdleSeconds = "120"
timeToLiveSeconds = "120"
overflowToDisk = "true"
/>
<cache name = "Employee"
maxElementsInMemory = "500"
eternal = "true"
timeToIdleSeconds = "0"
timeToLiveSeconds = "0"
overflowToDisk = "false"
/>
就是这样,现在我们为 Employee 类和 Hibernate 启用了二级缓存,现在每当我们导航到 Employee 或通过标识符加载 Employee 时都会命中二级缓存。
我们应该分析所有类并为每个类选择适当的缓存策略。 有时,二级缓存可能会降低应用程序的性能。 因此,建议先对我们的应用程序进行基准测试,而不启用缓存,然后再启用适合自己的缓存并检查性能。 如果缓存没有提高系统性能,那么启用任何类型的缓存都是没有意义的。
查询级缓存
要使用查询缓存,我们必须首先使用配置文件中的 hibernate.cache.use_query_cache="true"
属性激活它。 通过将此属性设置为 true,我们可以让 Hibernate 在内存中创建必要的缓存来保存查询和标识符集。
接下来,要使用查询缓存,请使用 Query 类的 setCacheable(Boolean)
方法。 例如
Session session = SessionFactory.openSession();
Query query = session.createQuery("FROM employee");
query.setCacheable(true);
List users = query.list();
SessionFactory.closeSession();
Hibernate 还通过缓存区域的概念支持非常细粒度的缓存支持。 缓存区域是指定名称的缓存的一部分。
Session session = SessionFactory.openSession();
Query query = session.createQuery("FROM employee");
query.setCacheable(true);
query.setCacheRegion("employee");
List users = query.list();
SessionFactory.closeSession();