Hibernate EHCache - Hibernate 二级缓存

欢迎来到Hibernate二级缓存示例教程。今天,我们将介绍Hibernate EHCache,它是最流行的Hibernate二级缓存提供程序。

Hibernate二级缓存

Hibernate二级缓存,Hibernate ehcacheExample,Hibernate缓存,EHCache example在大型应用程序中使用Hibernate的主要好处之一是它支持缓存,因此减少了数据库查询并提高了性能。在前面的例子中,我们研究了Hibernate First Level Cache,今天我们将使用Hibernate EHCache 实现来研究Hibernate二级缓存。Hibernate二级缓存提供程序包括EHCache和Infinisspan,但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 Example,Hibernate EHCache让我们逐一查看应用程序的每个组件。

Hibernate EHCache Maven配置

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

 1<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
 2    xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
 3    <modelVersion>4.0.0</modelVersion>
 4    <groupId>com.journaldev.hibernate</groupId>
 5    <artifactId>HibernateEHCacheExample</artifactId>
 6    <version>0.0.1-SNAPSHOT</version>
 7    <description>Hibernate Secondary Level Cache Example using EHCache implementation</description>
 8
 9    <dependencies>
10    	<!-- Hibernate Core API -->
11    	<dependency>
12    		<groupId>org.hibernate</groupId>
13    		<artifactId>hibernate-core</artifactId>
14    		<version>4.3.5.Final</version>
15    	</dependency>
16    	<!-- MySQL Driver -->
17    	<dependency>
18    		<groupId>mysql</groupId>
19    		<artifactId>mysql-connector-java</artifactId>
20    		<version>5.0.5</version>
21    	</dependency>
22    	<!-- EHCache Core APIs -->
23    	<dependency>
24    		<groupId>net.sf.ehcache</groupId>
25    		<artifactId>ehcache-core</artifactId>
26    		<version>2.6.9</version>
27    	</dependency>
28    	<!-- Hibernate EHCache API -->
29    	<dependency>
30    		<groupId>org.hibernate</groupId>
31    		<artifactId>hibernate-ehcache</artifactId>
32    		<version>4.3.5.Final</version>
33    	</dependency>
34    	<!-- EHCache uses slf4j for logging -->
35    	<dependency>
36    		<groupId>org.slf4j</groupId>
37    		<artifactId>slf4j-simple</artifactId>
38    		<version>1.7.5</version>
39    	</dependency>
40    </dependencies>
41</project>

Hibernate二级缓存-Hibernate EHCache配置

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

 1<?xml version="1.0" encoding="UTF-8"?>
 2<!DOCTYPE hibernate-configuration SYSTEM "classpath://org/hibernate/hibernate-configuration-3.0.dtd">
 3<hibernate-configuration>
 4    <session-factory>
 5    	<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
 6    	<property name="hibernate.connection.password">pankaj123</property>
 7    	<property name="hibernate.connection.url">jdbc:mysql://localhost/TestDB</property>
 8    	<property name="hibernate.connection.username">pankaj</property>
 9    	<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
10
11    	<property name="hibernate.current_session_context_class">thread</property>
12    	<property name="hibernate.show_sql">true</property>
13    	
14    	<property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
15    	
16    	<!-- For singleton factory -->
17    	<!-- <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory</property>
18    	 -->
19    	 
20    	 <!-- enable second level cache and query cache -->
21    	 <property name="hibernate.cache.use_second_level_cache">true</property>
22    	 <property name="hibernate.cache.use_query_cache">true</property>
23    	 <property name="net.sf.ehcache.configurationResourceName">/myehcache.xml</property>
24
25    	<mapping class="com.journaldev.hibernate.model.Employee" />
26    	<mapping class="com.journaldev.hibernate.model.Address" />
27    </session-factory>
28</hibernate-configuration>

有关Hibernate二级缓存配置的一些要点如下:

1.hibernate.cache.Region.Factory_CLASS 用于定义二级缓存的Factory类,我使用的是org.hibernate.cache.ehcache.EhCacheRegionFactory。如果希望工厂类是单例的,则应该使用org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory类。如果您使用的是Hibernate3,则对应的类将是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.configuration ationResourceName 用于定义EHCache配置文件的位置,是可选参数,如果不存在,EHCache会尝试在应用程序类路径中定位ehcache.xml文件。

Hibernate EHCache配置文件

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

 1<?xml version="1.0" encoding="UTF-8"?>
 2<ehcache xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
 3    xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="true"
 4    monitoring="autodetect" dynamicConfig="true">
 5
 6    <diskStore path="java.io.tmpdir/ehcache" />
 7
 8    <defaultCache maxEntriesLocalHeap="10000" eternal="false"
 9    	timeToIdleSeconds="120" timeToLiveSeconds="120" diskSpoolBufferSizeMB="30"
10    	maxEntriesLocalDisk="10000000" diskExpiryThreadIntervalSeconds="120"
11    	memoryStoreEvictionPolicy="LRU" statistics="true">
12    	<persistence strategy="localTempSwap" />
13    </defaultCache>
14
15    <cache name="employee" maxEntriesLocalHeap="10000" eternal="false"
16    	timeToIdleSeconds="5" timeToLiveSeconds="10">
17    	<persistence strategy="localTempSwap" />
18    </cache>
19
20    <cache name="org.hibernate.cache.internal.StandardQueryCache"
21    	maxEntriesLocalHeap="5" eternal="false" timeToLiveSeconds="120">
22    	<persistence strategy="localTempSwap" />
23    </cache>
24
25    <cache name="org.hibernate.cache.spi.UpdateTimestampsCache"
26    	maxEntriesLocalHeap="5000" eternal="true">
27    	<persistence strategy="localTempSwap" />
28    </cache>
29</ehcache>

Hibernate EHCache提供了很多选项,我不会深入讨论,但上面的一些重要配置如下:

1.diskStore :EHCache将数据存储到内存中,但当内存开始溢出时,它会开始向文件系统写入数据。我们使用此属性来定义EHCache将写入溢出数据的位置。 2.defaultCache :强制配置,对象需要缓存且未定义缓存区域时使用。 3.缓存名称=Employee :我们使用缓存元素来定义地域及其配置。我们可以定义多个区域及其属性,在定义模型Bean缓存属性的同时,还可以定义具有缓存策略的区域。缓存属性很容易理解,名称也很清楚。 4.定义缓存区org.hibernate.cache.internal.StandardQueryCacheorg.hibernate.cache.spi.UpdateTimestampsCache是因为EHCache对此发出了警告。

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

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

 1package com.journaldev.hibernate.model;
 2
 3import javax.persistence.Column;
 4import javax.persistence.Entity;
 5import javax.persistence.GeneratedValue;
 6import javax.persistence.Id;
 7import javax.persistence.OneToOne;
 8import javax.persistence.PrimaryKeyJoinColumn;
 9import javax.persistence.Table;
10
11import org.hibernate.annotations.Cache;
12import org.hibernate.annotations.CacheConcurrencyStrategy;
13import org.hibernate.annotations.GenericGenerator;
14import org.hibernate.annotations.Parameter;
15
16@Entity
17@Table(name = "ADDRESS")
18@Cache(usage=CacheConcurrencyStrategy.READ_ONLY, region="employee")
19public class Address {
20
21    @Id
22    @Column(name = "emp_id", unique = true, nullable = false)
23    @GeneratedValue(generator = "gen")
24    @GenericGenerator(name = "gen", strategy = "foreign", 
25    			parameters = { @Parameter(name = "property", value = "employee") })
26    private long id;
27
28    @Column(name = "address_line1")
29    private String addressLine1;
30
31    @Column(name = "zipcode")
32    private String zipcode;
33
34    @Column(name = "city")
35    private String city;
36
37    @OneToOne
38    @PrimaryKeyJoinColumn
39    private Employee employee;
40
41    public long getId() {
42    	return id;
43    }
44
45    public void setId(long id) {
46    	this.id = id;
47    }
48
49    public String getAddressLine1() {
50    	return addressLine1;
51    }
52
53    public void setAddressLine1(String addressLine1) {
54    	this.addressLine1 = addressLine1;
55    }
56
57    public String getZipcode() {
58    	return zipcode;
59    }
60
61    public void setZipcode(String zipcode) {
62    	this.zipcode = zipcode;
63    }
64
65    public String getCity() {
66    	return city;
67    }
68
69    public void setCity(String city) {
70    	this.city = city;
71    }
72
73    public Employee getEmployee() {
74    	return employee;
75    }
76
77    public void setEmployee(Employee employee) {
78    	this.employee = employee;
79    }
80
81}
 1package com.journaldev.hibernate.model;
 2
 3import javax.persistence.Column;
 4import javax.persistence.Entity;
 5import javax.persistence.GeneratedValue;
 6import javax.persistence.GenerationType;
 7import javax.persistence.Id;
 8import javax.persistence.OneToOne;
 9import javax.persistence.Table;
10
11import org.hibernate.annotations.Cache;
12import org.hibernate.annotations.CacheConcurrencyStrategy;
13import org.hibernate.annotations.Cascade;
14
15@Entity
16@Table(name = "EMPLOYEE")
17@Cache(usage=CacheConcurrencyStrategy.READ_ONLY, region="employee")
18public class Employee {
19
20    @Id
21    @GeneratedValue(strategy = GenerationType.IDENTITY)
22    @Column(name = "emp_id")
23    private long id;
24
25    @Column(name = "emp_name")
26    private String name;
27
28    @Column(name = "emp_salary")
29    private double salary;
30
31    @OneToOne(mappedBy = "employee")
32    @Cascade(value = org.hibernate.annotations.CascadeType.ALL)
33    private Address address;
34
35    public long getId() {
36    	return id;
37    }
38
39    public void setId(long id) {
40    	this.id = id;
41    }
42
43    public Address getAddress() {
44    	return address;
45    }
46
47    public void setAddress(Address address) {
48    	this.address = address;
49    }
50
51    public String getName() {
52    	return name;
53    }
54
55    public void setName(String name) {
56    	this.name = name;
57    }
58
59    public double getSalary() {
60    	return salary;
61    }
62
63    public void setSalary(double salary) {
64    	this.salary = salary;
65    }
66
67}

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

Hibernate SessionFactory实用程序类

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

 1package com.journaldev.hibernate.util;
 2
 3import org.hibernate.SessionFactory;
 4import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
 5import org.hibernate.cfg.Configuration;
 6import org.hibernate.service.ServiceRegistry;
 7
 8public class HibernateUtil {
 9
10    private static SessionFactory sessionFactory;
11    
12    private static SessionFactory buildSessionFactory() {
13        try {
14            // Create the SessionFactory from hibernate.cfg.xml
15        	Configuration configuration = new Configuration();
16        	configuration.configure("hibernate.cfg.xml");
17        	System.out.println("Hibernate Configuration loaded");
18        	
19        	ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
20        	System.out.println("Hibernate serviceRegistry created");
21        	
22        	SessionFactory sessionFactory = configuration.buildSessionFactory(serviceRegistry);
23        	
24            return sessionFactory;
25        }
26        catch (Throwable ex) {
27            System.err.println("Initial SessionFactory creation failed." + ex);
28            ex.printStackTrace();
29            throw new ExceptionInInitializerError(ex);
30        }
31    }
32    
33    public static SessionFactory getSessionFactory() {
34    	if(sessionFactory == null) sessionFactory = buildSessionFactory();
35        return sessionFactory;
36    }
37}

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

Hibernate EHCache测试程序

 1package com.journaldev.hibernate.main;
 2
 3import org.hibernate.Session;
 4import org.hibernate.SessionFactory;
 5import org.hibernate.Transaction;
 6import org.hibernate.stat.Statistics;
 7
 8import com.journaldev.hibernate.model.Employee;
 9import com.journaldev.hibernate.util.HibernateUtil;
10
11public class HibernateEHCacheMain {
12
13    public static void main(String[] args) {
14    	
15    	System.out.println("Temp Dir:"+System.getProperty("java.io.tmpdir"));
16    	
17    	//Initialize Sessions
18    	SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
19    	Statistics stats = sessionFactory.getStatistics();
20    	System.out.println("Stats enabled="+stats.isStatisticsEnabled());
21    	stats.setStatisticsEnabled(true);
22    	System.out.println("Stats enabled="+stats.isStatisticsEnabled());
23    	
24    	Session session = sessionFactory.openSession();
25    	Session otherSession = sessionFactory.openSession();
26    	Transaction transaction = session.beginTransaction();
27    	Transaction otherTransaction = otherSession.beginTransaction();
28    	
29    	printStats(stats, 0);
30    	
31    	Employee emp = (Employee) session.load(Employee.class, 1L);
32    	printData(emp, stats, 1);
33    	
34    	emp = (Employee) session.load(Employee.class, 1L);
35    	printData(emp, stats, 2);
36    	
37    	//clear first level cache, so that second level cache is used
38    	session.evict(emp);
39    	emp = (Employee) session.load(Employee.class, 1L);
40    	printData(emp, stats, 3);
41    	
42    	emp = (Employee) session.load(Employee.class, 3L);
43    	printData(emp, stats, 4);
44    	
45    	emp = (Employee) otherSession.load(Employee.class, 1L);
46    	printData(emp, stats, 5);
47    	
48    	//Release resources
49    	transaction.commit();
50    	otherTransaction.commit();
51    	sessionFactory.close();
52    }
53
54    private static void printStats(Statistics stats, int i) {
55    	System.out.println("***** " + i + " *****");
56    	System.out.println("Fetch Count="
57    			+ stats.getEntityFetchCount());
58    	System.out.println("Second Level Hit Count="
59    			+ stats.getSecondLevelCacheHitCount());
60    	System.out
61    			.println("Second Level Miss Count="
62    					+ stats
63    							.getSecondLevelCacheMissCount());
64    	System.out.println("Second Level Put Count="
65    			+ stats.getSecondLevelCachePutCount());
66    }
67
68    private static void printData(Employee emp, Statistics stats, int count) {
69    	System.out.println(count+":: Name="+emp.getName()+", Zipcode="+emp.getAddress().getZipcode());
70    	printStats(stats, count);
71    }
72
73}

org.hibernate.stat.spatitics提供了Hibernate SessionFactory的统计数据,我们用它来打印FETCH COUNT和二级CACHE HIT,MISS,PUT COUNT。为了获得更好的性能,默认情况下禁用统计信息,这就是我在程序开始时启用它的原因。当我们运行上面的程序时,我们得到了由Hibernate和EHCachAPI生成的大量输出,但我们对打印的数据感兴趣。样例运行将在输出后打印。

 1Temp Dir:/var/folders/h4/q73jjy0902g51wkw0w69c0600000gn/T/
 2Hibernate Configuration loaded
 3Hibernate serviceRegistry created
 4Stats enabled=false
 5Stats enabled=true
 6***** 0 *****
 7Fetch Count=0
 8Second Level Hit Count=0
 9Second Level Miss Count=0
10Second Level Put Count=0
11Hibernate: 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=?
121:: Name=Pankaj, Zipcode=95129
13***** 1 *****
14Fetch Count=1
15Second Level Hit Count=0
16Second Level Miss Count=1
17Second Level Put Count=2
182:: Name=Pankaj, Zipcode=95129
19***** 2 *****
20Fetch Count=1
21Second Level Hit Count=0
22Second Level Miss Count=1
23Second Level Put Count=2
243:: Name=Pankaj, Zipcode=95129
25***** 3 *****
26Fetch Count=1
27Second Level Hit Count=2
28Second Level Miss Count=1
29Second Level Put Count=2
30Hibernate: 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=?
314:: Name=Lisa, Zipcode=560100
32***** 4 *****
33Fetch Count=2
34Second Level Hit Count=2
35Second Level Miss Count=2
36Second Level Put Count=4
375:: Name=Pankaj, Zipcode=95129
38***** 5 *****
39Fetch Count=2
40Second Level Hit Count=4
41Second Level Miss Count=2
42Second Level Put Count=4

从输出中可以看到,统计数据最初是被禁用的,但我们启用它来检查我们的休眠二级缓存。输出的逐步解释如下:

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

因此,很明显,我们的Hibernate二级缓存Hibernate EHCache工作得很好。Hibernate统计信息有助于找到系统中的瓶颈,并对其进行优化,以减少提取次数并从缓存加载更多数据。这就是Hibernate EHCache示例 的全部内容,我希望它将帮助您在Hibernate应用程序中配置EHCache,并通过Hibernate二级缓存获得更好的性能。您可以从下面的链接下载样例项目,并使用其他统计数据了解更多信息。

下载Hibernate EHCache Project

Published At
Categories with 技术
Tagged with
comments powered by Disqus