Hibernate EHCache - Hibernate二级缓存

 2023-09-06 阅读 20 评论 0

摘要:Hibernate EHCache - Hibernate二级缓存 欢迎使用Hibernate二级缓存示例教程。今天我们将研究Hibernate EHCache,它是最受欢迎的Hibernate二级缓存提供程序。 目录[隐藏] 1 Hibernate 二级缓存2Hibernate EHCache 2.1Hibernate EHCache Maven依赖项2.2 Hibernate 二级

 

Hibernate EHCache - Hibernate二级缓存

 

欢迎使用Hibernate二级缓存示例教程。今天我们将研究Hibernate EHCache,它是最受欢迎的Hibernate二级缓存提供程序。

目录[ 隐藏 ]

  • 1 Hibernate 二级缓存
  • 2 Hibernate EHCache
    • 2.1 Hibernate EHCache Maven依赖项
    • 2.2 Hibernate 二级缓存 - Hibernate EHCache配置
    • 2.3 Hibernate EHCache配置文件
    • 2.4 Hibernate 二级缓存 - 模型Bean缓存策略
    • 2.5 Hibernate SessionFactory实用工具类
    • 2.6 Hibernate EHCache测试程序

 

Hibernate 二级缓存

在大型应用程序中使用Hibernate的一个主要好处是它支持缓存,因此减少了数据库查询和更好的性能。在前面的例子中,我们研究了Hibernate First Level Cache,今天我们将使用Hibernate EHCache实现来研究Hibernate二级缓存。

Hibernate二级缓存提供程序包括EHCache和Infinispan,但EHCache更受欢迎,我们将它用于我们的示例项目。然而,在我们迁移到项目之前,我们应该知道缓存对象的不同策略。

  1. 只读:此缓存策略应该用于始终读取但从未更新的持久对象。它适用于读取和缓存应用程序配置以及从未更新的其他静态数据。这是具有最佳性能的最简单策略,因为没有重载来检查对象是否在数据库中更新。
  2. 读写:这对于可以由hibernate应用程序更新的持久对象很有用。但是,如果数据是通过后端或其他应用程序更新的,那么hibernate就无法知道它并且数据可能是陈旧的。因此,在使用此策略时,请确保使用Hibernate API更新数据。
  3. 非限制读写:如果应用程序仅偶尔需要更新数据并且不需要严格的事务隔离,则非限制读写缓存可能是合适的。
  4. 事务性:事务性缓存策略为完全事务性缓存提供程序(如JBoss TreeCache)提供支持。这样的缓存只能在JTA环境中使用,您必须指定hibernate.transaction.manager_lookup_class。

 

Hibernate EHCache

由于EHCache支持所有上述缓存策略,因此当您在hibernate中寻找二级缓存时,它是最佳选择。我不会详细介绍EHCache,我的主要重点是让它适用于hibernate应用程序。

在Eclipse或您最喜欢的IDE中创建一个maven项目,最终实现将如下图所示。

让我们逐个查看应用程序的每个组件。

 

Hibernate EHCache Maven依赖项

对于hibernate二级缓存,我们需要在我们的应用程序中添加ehcache-corehibernate-ehcache依赖项。EHCache使用slf4j进行日志记录,因此我还添加了slf4j-simple用于日志记录。我正在使用所有这些API的最新版本,hibernate-ehcache API与ehcache-core API不兼容,在这种情况下你需要检查hibernate-ehcache的pom.xml以找出正确的版本使用。我们的最终pom.xml如下所示。


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.journaldev.hibernate</groupId><artifactId>HibernateEHCacheExample</artifactId><version>0.0.1-SNAPSHOT</version><description>Hibernate Secondary Level Cache Example using EHCache implementation</description><dependencies><!-- Hibernate Core API --><dependency><groupId>org.hibernate</groupId><artifactId>hibernate-core</artifactId><version>4.3.5.Final</version></dependency><!-- MySQL Driver --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.0.5</version></dependency><!-- EHCache Core APIs --><dependency><groupId>net.sf.ehcache</groupId><artifactId>ehcache-core</artifactId><version>2.6.9</version></dependency><!-- Hibernate EHCache API --><dependency><groupId>org.hibernate</groupId><artifactId>hibernate-ehcache</artifactId><version>4.3.5.Final</version></dependency><!-- EHCache uses slf4j for logging --><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-simple</artifactId><version>1.7.5</version></dependency></dependencies>
</project>

Hibernate二级缓存 - Hibernate EHCache配置

Hibernate默认情况下禁用二级缓存,因此我们需要启用它并添加一些配置才能使其正常工作。我们的hibernate.cfg.xml文件如下所示。


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration SYSTEM "classpath://org/hibernate/hibernate-configuration-3.0.dtd">
<hibernate-configuration><session-factory><property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property><property name="hibernate.connection.password">pankaj123</property><property name="hibernate.connection.url">jdbc:mysql://localhost/TestDB</property><property name="hibernate.connection.username">pankaj</property><property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property><property name="hibernate.current_session_context_class">thread</property><property name="hibernate.show_sql">true</property><property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property><!-- For singleton factory --><!-- <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory</property>--><!-- enable second level cache and query cache --><property name="hibernate.cache.use_second_level_cache">true</property><property name="hibernate.cache.use_query_cache">true</property><property name="net.sf.ehcache.configurationResourceName">/myehcache.xml</property><mapping class="com.journaldev.hibernate.model.Employee" /><mapping class="com.journaldev.hibernate.model.Address" /></session-factory>
</hibernate-configuration>

关于休眠二级缓存配置的一些重要观点是:

  1. hibernate.cache.region.factory_class用于为二级缓存定义Factory类,我正在使用org.hibernate.cache.ehcache.EhCacheRegionFactory它。如果您希望工厂类是单例,则应使用org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory类。

    如果您使用的是Hibernate 3,则相应的类将为net.sf.ehcache.hibernate.EhCacheRegionFactorynet.sf.ehcache.hibernate.SingletonEhCacheRegionFactory

  2. hibernate.cache.use_second_level_cache用于启用二级缓存。
  3. hibernate.cache.use_query_cache用于启用查询缓存,如果没有它,HQL查询结果将不会被缓存。
  4. net.sf.ehcache.configurationResourceName用于定义EHCache配置文件位置,它是一个可选参数,如果不存在,EHCache将尝试在应用程序类路径中找到ehcache.xml文件。

 

Hibernate EHCache配置文件

我们的EHCache配置文件myehcache.xml如下所示。


<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="true"monitoring="autodetect" dynamicConfig="true"><diskStore path="java.io.tmpdir/ehcache" /><defaultCache maxEntriesLocalHeap="10000" eternal="false"timeToIdleSeconds="120" timeToLiveSeconds="120" diskSpoolBufferSizeMB="30"maxEntriesLocalDisk="10000000" diskExpiryThreadIntervalSeconds="120"memoryStoreEvictionPolicy="LRU" statistics="true"><persistence strategy="localTempSwap" /></defaultCache><cache name="employee" maxEntriesLocalHeap="10000" eternal="false"timeToIdleSeconds="5" timeToLiveSeconds="10"><persistence strategy="localTempSwap" /></cache><cache name="org.hibernate.cache.internal.StandardQueryCache"maxEntriesLocalHeap="5" eternal="false" timeToLiveSeconds="120"><persistence strategy="localTempSwap" /></cache><cache name="org.hibernate.cache.spi.UpdateTimestampsCache"maxEntriesLocalHeap="5000" eternal="true"><persistence strategy="localTempSwap" /></cache>
</ehcache>

Hibernate EHCache提供了很多选项,我不会详细介绍,但上面的一些重要配置是:

  1. diskStore:EHCache将数据存储到内存中,但当它开始溢出时,它开始将数据写入文件系统。我们使用此属性来定义EHCache将写入溢出数据的位置。
  2. defaultCache:这是一个强制配置,当需要缓存一个Object并且没有为其定义缓存区域时使用它。
  3. cache name =“employee”:我们使用cache元素来定义区域及其配置。我们可以定义多个区域及其属性,同时定义模型bean缓存属性,我们也可以使用缓存策略定义区域。缓存属性易于理解,并且名称清晰。
  4. 缓存区域org.hibernate.cache.internal.StandardQueryCacheorg.hibernate.cache.spi.UpdateTimestampsCache定义因为EHCache正在发出警告。

 

Hibernate二级缓存 - 模型Bean缓存策略

我们使用org.hibernate.annotations.Cache注释来提供缓存配置。org.hibernate.annotations.CacheConcurrencyStrategy用于定义缓存策略,我们还可以定义用于模型bean的缓存区域。

 


package com.journaldev.hibernate.model;import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.Table;import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Parameter;@Entity
@Table(name = "ADDRESS")
@Cache(usage=CacheConcurrencyStrategy.READ_ONLY, region="employee")
public class Address {@Id@Column(name = "emp_id", unique = true, nullable = false)@GeneratedValue(generator = "gen")@GenericGenerator(name = "gen", strategy = "foreign", parameters = { @Parameter(name = "property", value = "employee") })private long id;@Column(name = "address_line1")private String addressLine1;@Column(name = "zipcode")private String zipcode;@Column(name = "city")private String city;@OneToOne@PrimaryKeyJoinColumnprivate Employee employee;public long getId() {return id;}public void setId(long id) {this.id = id;}public String getAddressLine1() {return addressLine1;}public void setAddressLine1(String addressLine1) {this.addressLine1 = addressLine1;}public String getZipcode() {return zipcode;}public void setZipcode(String zipcode) {this.zipcode = zipcode;}public String getCity() {return city;}public void setCity(String city) {this.city = city;}public Employee getEmployee() {return employee;}public void setEmployee(Employee employee) {this.employee = employee;}}

package com.journaldev.hibernate.model;import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.Table;import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.Cascade;@Entity
@Table(name = "EMPLOYEE")
@Cache(usage=CacheConcurrencyStrategy.READ_ONLY, region="employee")
public class Employee {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)@Column(name = "emp_id")private long id;@Column(name = "emp_name")private String name;@Column(name = "emp_salary")private double salary;@OneToOne(mappedBy = "employee")@Cascade(value = org.hibernate.annotations.CascadeType.ALL)private Address address;public long getId() {return id;}public void setId(long id) {this.id = id;}public Address getAddress() {return address;}public void setAddress(Address address) {this.address = address;}public String getName() {return name;}public void setName(String name) {this.name = name;}public double getSalary() {return salary;}public void setSalary(double salary) {this.salary = salary;}}

请注意,我使用的是与HQL示例中相同的数据库设置,您可能需要检查以创建数据库表并加载示例数据。

Hibernate SessionFactory实用工具类

我们有一个简单的实用程序类来配置hibernate并获取SessionFactory单例实例。


package com.journaldev.hibernate.util;import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;public class HibernateUtil {private static SessionFactory sessionFactory;private static SessionFactory buildSessionFactory() {try {// Create the SessionFactory from hibernate.cfg.xmlConfiguration configuration = new Configuration();configuration.configure("hibernate.cfg.xml");System.out.println("Hibernate Configuration loaded");ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();System.out.println("Hibernate serviceRegistry created");SessionFactory sessionFactory = configuration.buildSessionFactory(serviceRegistry);return sessionFactory;}catch (Throwable ex) {System.err.println("Initial SessionFactory creation failed." + ex);ex.printStackTrace();throw new ExceptionInInitializerError(ex);}}public static SessionFactory getSessionFactory() {if(sessionFactory == null) sessionFactory = buildSessionFactory();return sessionFactory;}
}

我们使用Hibernate EHCache的hibernate二级缓存项目已准备就绪,让我们编写一个简单的程序来测试它。

 

Hibernate EHCache测试程序


package com.journaldev.hibernate.main;import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.stat.Statistics;import com.journaldev.hibernate.model.Employee;
import com.journaldev.hibernate.util.HibernateUtil;public class HibernateEHCacheMain {public static void main(String[] args) {System.out.println("Temp Dir:"+System.getProperty("java.io.tmpdir"));//Initialize SessionsSessionFactory sessionFactory = HibernateUtil.getSessionFactory();Statistics stats = sessionFactory.getStatistics();System.out.println("Stats enabled="+stats.isStatisticsEnabled());stats.setStatisticsEnabled(true);System.out.println("Stats enabled="+stats.isStatisticsEnabled());Session session = sessionFactory.openSession();Session otherSession = sessionFactory.openSession();Transaction transaction = session.beginTransaction();Transaction otherTransaction = otherSession.beginTransaction();printStats(stats, 0);Employee emp = (Employee) session.load(Employee.class, 1L);printData(emp, stats, 1);emp = (Employee) session.load(Employee.class, 1L);printData(emp, stats, 2);//clear first level cache, so that second level cache is usedsession.evict(emp);emp = (Employee) session.load(Employee.class, 1L);printData(emp, stats, 3);emp = (Employee) session.load(Employee.class, 3L);printData(emp, stats, 4);emp = (Employee) otherSession.load(Employee.class, 1L);printData(emp, stats, 5);//Release resourcestransaction.commit();otherTransaction.commit();sessionFactory.close();}private static void printStats(Statistics stats, int i) {System.out.println("***** " + i + " *****");System.out.println("Fetch Count="+ stats.getEntityFetchCount());System.out.println("Second Level Hit Count="+ stats.getSecondLevelCacheHitCount());System.out.println("Second Level Miss Count="+ stats.getSecondLevelCacheMissCount());System.out.println("Second Level Put Count="+ stats.getSecondLevelCachePutCount());}private static void printData(Employee emp, Statistics stats, int count) {System.out.println(count+":: Name="+emp.getName()+", Zipcode="+emp.getAddress().getZipcode());printStats(stats, count);}}

org.hibernate.stat.Statistics提供Hibernate SessionFactory的统计信息,我们用它来打印获取计数和二级缓存命中,未命中和放置计数。默认情况下禁用统计信息以获得更好的性能,这就是我在程序开始时启用它的原因。

当我们运行上面的程序时,我们会获得Hibernate和EHCache API生成的大量输出,但我们对正在打印的数据感兴趣。样本运行打印输出。


Temp Dir:/var/folders/h4/q73jjy0902g51wkw0w69c0600000gn/T/
Hibernate Configuration loaded
Hibernate serviceRegistry created
Stats enabled=false
Stats enabled=true
***** 0 *****
Fetch Count=0
Second Level Hit Count=0
Second Level Miss Count=0
Second Level Put Count=0
Hibernate: select employee0_.emp_id as emp_id1_1_0_, employee0_.emp_name as emp_name2_1_0_, employee0_.emp_salary as emp_sala3_1_0_, address1_.emp_id as emp_id1_0_1_, address1_.address_line1 as address_2_0_1_, address1_.city as city3_0_1_, address1_.zipcode as zipcode4_0_1_ from EMPLOYEE employee0_ left outer join ADDRESS address1_ on employee0_.emp_id=address1_.emp_id where employee0_.emp_id=?
1:: Name=Pankaj, Zipcode=95129
***** 1 *****
Fetch Count=1
Second Level Hit Count=0
Second Level Miss Count=1
Second Level Put Count=2
2:: Name=Pankaj, Zipcode=95129
***** 2 *****
Fetch Count=1
Second Level Hit Count=0
Second Level Miss Count=1
Second Level Put Count=2
3:: Name=Pankaj, Zipcode=95129
***** 3 *****
Fetch Count=1
Second Level Hit Count=2
Second Level Miss Count=1
Second Level Put Count=2
Hibernate: select employee0_.emp_id as emp_id1_1_0_, employee0_.emp_name as emp_name2_1_0_, employee0_.emp_salary as emp_sala3_1_0_, address1_.emp_id as emp_id1_0_1_, address1_.address_line1 as address_2_0_1_, address1_.city as city3_0_1_, address1_.zipcode as zipcode4_0_1_ from EMPLOYEE employee0_ left outer join ADDRESS address1_ on employee0_.emp_id=address1_.emp_id where employee0_.emp_id=?
4:: Name=Lisa, Zipcode=560100
***** 4 *****
Fetch Count=2
Second Level Hit Count=2
Second Level Miss Count=2
Second Level Put Count=4
5:: Name=Pankaj, Zipcode=95129
***** 5 *****
Fetch Count=2
Second Level Hit Count=4
Second Level Miss Count=2
Second Level Put Count=4

从输出中可以看出,统计信息最初被禁用,但我们启用它来检查我们的休眠二级缓存。

输出的逐步说明如下:

  1. 在我们在应用程序中加载任何数据之前,所有统计信息都是0。
  2. 当我们第一次加载id = 1的Employee时,它首先搜索到第一级缓存,然后是第二级缓存。如果在缓存中找不到,则执行数据库查询,因此获取计数变为1.一旦加载了对象,它就被保存到第一级缓存和第二级缓存中。因此,二级命中计数保持为0且未命中计数为1.请注意,put count为2,这是因为Employee对象也包含Address,因此两个对象都保存到二级缓存中,并且计数增加到2。
  3. 接下来,我们再次加载id = 1的员工,这次它出现在第一级缓存中。因此,您没有看到任何数据库查询,并且所有其他二级缓存统计信息也保持不变。
  4. 接下来我们使用evict()方法从第一级缓存中删除employee对象,现在当我们尝试加载它时,hibernate在二级缓存中找到它。这就是为什么没有触发数据库查询并且获取计数仍为1.请注意,命中计数从0变为2,因为从二级缓存中读取了Employee和Address对象。二级未命中和计数仍保持在较早值。
  5. 接下来,我们正在加载id = 3的员工,执行数据库查询并且获取计数增加到2,未命中计数从1增加到2并且将计数从2增加到4。
  6. 接下来,我们尝试在另一个会话中加载id = 1的员工,因为hibernate二级缓存是跨会话共享的,所以它位于二级缓存中,并且不执行任何数据库查询。获取计数,未命中计数和放置计数保持不变,而命中计数从2增加到4。

所以很明显我们的Hibernate二级缓存; Hibernate EHCache; 工作正常。Hibernate统计信息有助于找到系统中的瓶颈并对其进行优化,以减少获取次数并从缓存中加载更多数据。

这就是Hibernate EHCache示例的全部内容,我希望它能帮助您在hibernate应用程序中配置EHCache并通过hibernate二级缓存获得更好的性能。您可以从下面的链接下载示例项目,并使用其他统计数据来了解更多信息。

下载Hibernate EHCache项目

 

转载来源:https://www.journaldev.com/2980/hibernate-ehcache-hibernate-second-level-cache

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/2/4043.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息