hibernate ehcache 配置指导

缓存由ORM框架提供以便用户可以获得更快速的访问速度,同时框架本身也减少了数据库查询操作。hibernate同样在两个层上提供了缓存的功能。

一级缓存:这是在session范围内默认使用的,了解更多可以读这篇文章。

二级缓存:这是在一级缓存之上的缓存,在session factory 范围内有效。

在这篇文章中,我将给出使用ehcache在hibernate配置二级缓存的例子。

hibernate ehcache 配置指导_第1张图片

这篇文章的结构:

二级缓存是如何工作的

关于ehcache

配置ehcache

配置实体对象

查询缓存

应用例子

源码下载

二级缓存是如何工作的

1、当hibernate Session试着加载实体的时候,会从一级缓存中获取对象的复制(与特定的hibernate Session相关连)
2、如果在一级缓存中存在实体的复制,将会把实体结果返回。
3、如果在一级缓存中不存在缓存的实体,二级缓存会查找是不是有缓存的实体。
4、如果二级缓存中有缓存的实体,将会把实体的结果返回。但是,在返回实体之前,它将会把结果存储到一级缓存中去,下次调用时可以直接从一级缓存中获取相应的结果,这样就没有必要再想二级缓存中获取。
5、如果在一级缓存和二级缓存中都没有找到相应的实体,那么数据库查询语句将会执行,在返回结果之前,实体将会被存储在一二级缓存中。
6、如果通过hibernate操作修改已经完成,对于修改的实体二级缓存会完成相应的验证。
7、如果用户或者进程直接在数据库中做了修改,二级缓存不能更新自己直到timeToLiveSeconds时间结束。在这种情况下最好将所有的缓存失效并让hibernate再一次建立自己的缓存,你可以使用下边的代码片段失效整个hibernate的二级缓存。

关于更多二级缓存是如何工作的,可以参考这篇文章。

关于ehcache

Terracotta  ehcache是一个流行的开源的Java缓存,可以被用在hibernate的二级缓存中,ehcache也可以被当做独立的二级缓存使用,或者为集群配置提供二级缓存。hibernate可以使用ehcache的库,如果你想要ehcache的某一个具体的版本,可以访问网址
http://www.terracotta.org/products/enterprise-ehcache
ehcache2.0的maven依赖如下:
<dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>[2.0.0]</version>
    <type>pom</type>
</dependency>

配置ehcache

为了配置ehcache,你需要做下边两个步骤的事情:
1、为二级缓存配置hibernate
2、制定二级缓存的提供者
hibernate3.3以上的版本
<property key="hibernate.cache.use_second_level_cache">true</property>
<property name="hibernate.cache.region.factory_class">net.sf.ehcache.hibernate.EhCacheRegionFactory</property>

hibernate3.2和以下版本
<property key="hibernate.cache.use_second_level_cache">true</property>
<property name="hibernate.cache.region.provider_class">net.sf.ehcache.hibernate.EhCacheProvider</property>

配置实体对象

可以使用两种方式
(1)如果使用hbm.xml文件,使用下边的配置
<class name="com.application.entity.DepartmentEntity" table="...">
    <cache usage="read-write"/>
</class>

(2)如果使用注解,是用下边这些注解
@Entity
@Cache(usage=CacheConcurrencyStrategy.READ_ONLY,
region="department")
public class DepartmentEntity implements Serializable
{
    //code
}

对于上边这两种方式,缓存策略可以是下边的类型:
none:不会有缓存发生
read-only:如果你的应用需要读而不是修改,持久类的实例上可以使用read-only缓存
read-write:如果你的缓存需要更新数据,一个read-write缓存是很合适的。
nonstrict-read-write:如果你的应用只在特定的情况下更新数据(例如:一个极特殊的情况,两个事物同时更新数据),并且没有严格是事务独立要求,nonstrict-read-write或许就是合适的选择。
transactional:事务缓存策略为为所有的事务缓存提供者提供支持,比如一个缓存可以仅仅在JTA中使用,你必须指定hibernate.transaction.manager_lookup_class.

查询缓存

你可是使用在hbm.xml中配置如下的语句使查询缓存生效
<property key="hibernate.cache.use_query_cache">true</property>

在你的代码中定义的查询上增加方法setCacheable(true),这样就可以缓存了。
sessionFactory.getCurrentSession().createQuery("...").setCacheable(true).list();

默认情况下,ehcache会为你配置的需要缓存每一个实体划分出不同的缓存区域。你可以在ehcache.xml中增加配置来改变这个默认的区域。
<property name="net.sf.ehcache.configurationResourceName">/ehcache.xml</property>

使用下边的配置覆盖默认的配置
<cache
    name="com.somecompany.someproject.domain.Country"
    maxElementsInMemory="10000"
    eternal="false"
    timeToIdleSeconds="300"
    timeToLiveSeconds="600"
    overflowToDisk="true"
/>

在ehcache.xml中需要注意:如果eternal="true"那么我们不需要写 timeToIdealSeconds, timeToLiveSeconds,hibernate会处理这些值,如果你想人为的给定缓存的值最好还是使用上边的那种配置方式,这样我们就可以手动的将值指定 timeToIdealSeconds,timeToLiveSeconds 。
timeToIdealSeconds=”seconds”意味着,如果对象在全局缓存中是理想的,在全局中使用的类或者对象将会等待超过我们设置的timeToIdealSeconds 时间值将缓存的值删除。
timeToLiveSeconds=”seconds” 意味着,其他的session或者类使用不是使用这个对象,超过这个时间,hibernate将会从全局缓存中将它删除。

例子

在我们的应用例子中,我有一个DepartmentEntity,在这上边使用ehcache的二级缓存,让我们一步一步操作这个事情
1) hibernate.cfg.xml:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hibernatedemo</property>
        <property name="hibernate.connection.password">password</property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
        <property name="show_sql">true</property>
        <property name="hbm2ddl.auto">create</property>
        <property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
        <mapping class="hibernate.test.dto.DepartmentEntity"></mapping>
    </session-factory>
</hibernate-configuration>

2) DepartmentEntity.java:
package hibernate.test.dto;
 
import java.io.Serializable;
 
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
 
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
 
@Entity (name = "dept")
@Table(name = "DEPARTMENT", uniqueConstraints = {
        @UniqueConstraint(columnNames = "ID"),
        @UniqueConstraint(columnNames = "NAME") })
         
@Cache(usage=CacheConcurrencyStrategy.READ_ONLY, region="department")
 
public class DepartmentEntity implements Serializable {
     
    private static final long serialVersionUID = 1L;
 
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ID", unique = true, nullable = false)
    private Integer id;
     
    @Column(name = "NAME", unique = true, nullable = false, length = 100)
    private String name;
 
    public Integer getId() {
        return id;
    }
 
    public void setId(Integer id) {
        this.id = id;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
}

3) HibernateUtil.java:
package hibernate.test;
 
import java.io.File;
 
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;
 
public class HibernateUtil
{
    private static final SessionFactory sessionFactory = buildSessionFactory();
      
    private static SessionFactory buildSessionFactory()
    {
        try
        {
            // Create the SessionFactory from hibernate.cfg.xml
            return new AnnotationConfiguration().configure(new File("hibernate.cgf.xml")).buildSessionFactory();
        }
        catch (Throwable ex) {
            // Make sure you log the exception, as it might be swallowed
            System.err.println("Initial SessionFactory creation failed." + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }
  
    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }
  
    public static void shutdown() {
        // Close caches and connection pools
        getSessionFactory().close();
    }
}

4) TestHibernateEhcache.java:
public class TestHibernateEhcache
{  
    public static void main(String[] args)
    {
        storeData();
         
        try
        {
            //Open the hibernate session
            Session session = HibernateUtil.getSessionFactory().openSession();
            session.beginTransaction();
             
            //fetch the department entity from database first time
            DepartmentEntity department = (DepartmentEntity) session.load(DepartmentEntity.class, new Integer(1));
            System.out.println(department.getName());
             
            //fetch the department entity again; Fetched from first level cache
            department = (DepartmentEntity) session.load(DepartmentEntity.class, new Integer(1));
            System.out.println(department.getName());
             
            //Let's close the session
            session.getTransaction().commit();
            session.close();
             
            //Try to get department in new session
            Session anotherSession = HibernateUtil.getSessionFactory().openSession();
            anotherSession.beginTransaction();
             
            //Here entity is already in second level cache so no database query will be hit
            department = (DepartmentEntity) anotherSession.load(DepartmentEntity.class, new Integer(1));
            System.out.println(department.getName());
             
            anotherSession.getTransaction().commit();
            anotherSession.close();
        }
        finally
        {
            System.out.println(HibernateUtil.getSessionFactory().getStatistics().getEntityFetchCount()); //Prints 1
            System.out.println(HibernateUtil.getSessionFactory().getStatistics().getSecondLevelCacheHitCount()); //Prints 1
             
            HibernateUtil.shutdown();
        }
    }
     
    private static void storeData()
    {
        Session session = HibernateUtil.getSessionFactory().openSession();
        session.beginTransaction();
         
        DepartmentEntity department = new DepartmentEntity();
        department.setName("Human Resource");
         
        session.save(department);
        session.getTransaction().commit();
    }
}
 
Output:
 
Hibernate: insert into DEPARTMENT (NAME) values (?)
Hibernate: select department0_.ID as ID0_0_, department0_.NAME as NAME0_0_ from DEPARTMENT department0_ where department0_.ID=?
Human Resource
Human Resource
Human Resource
1
1

在上边的输出结果中,我们可以看到,第一次department是从数据库中获取,下边两次是从缓存中获取,最后一次是从二级缓存中获取。
源码下载

你可能感兴趣的:(hibernate ehcache 配置指导)