欢迎来到Hibernate二级缓存示例教程。今天,我们将介绍Hibernate EHCache,它是最流行的Hibernate二级缓存提供程序。
Hibernate二级缓存
在大型应用程序中使用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 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.EhCacheRegionFactory
和net.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.StandardQueryCache
和org.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二级缓存获得更好的性能。您可以从下面的链接下载样例项目,并使用其他统计数据了解更多信息。