如何在JPA中使用Hibernate的二级缓存?

时间:2012-05-15 12:30:52

标签: java hibernate caching jpa nhibernate-caches

我正在实现基于实体属性值的持久性机制。所有数据库访问都是通过Hibernate完成的。 我有一个包含节点路径的表,它非常简单,只是一个id,一个路径(字符串)路径数量很少,大约有几千个。

主表有数百万行,而不是重复路径,我已经规范化了自己的表的路径。以下是我在插入主表时所需的行为

1)检查路径表中是否存在路径(通过实体管理器查询,使用路径值作为参数)

2)如果它不存在,则插入并获取id(通过实体管理器持久化)

3)将id作为外键值放入主表行,并将其插入主表。

对于一组域对象,这将发生数千次,对应于主表和其他一些表中的大量行。因此,使用如下的单个事务重复上述步骤:

    EntityTransaction t = entityManager.getTransaction();
    t.begin();
    //perform steps given above, check, and then persist etc..
    t.commit();

当我执行第2步时,它会给整个操作带来巨大的性能下降。它正在乞求缓存,因为过了一段时间,该表最多只有10-20k条目,非常罕见的新插入。我试图用Hibernate做到这一点,并且失去了将近2天。

我正在使用Hibernate 4.1,带有JPA注释和ECache。我试图启用查询缓存,甚至在整个插入过程中使用相同的查询对象,如下所示:

Query call = entityManager.createQuery("select pt from NodePath pt " +
                "where pt.path = :pathStr)");
        call.setHint("org.hibernate.cacheable", true);  
        call.setParameter("pathStr", pPath);
        List<NodePath> paths = call.getResultList();
        if(paths.size() > 1)
            throw new Exception("path table should have unique paths");
        else if (paths.size() == 1){
            NodePath path = paths.get(0);
            return path.getId();
        }
        else {//paths null or has zero size
            NodePath newPath = new NodePath();
            newPath.setPath(pPath);
            entityManager.persist(newPath);
            return newPath.getId();
        }

NodePath实体注释如下:

@Entity
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@Table(name = "node_path", schema = "public")
public class NodePath implements java.io.Serializable {

正如我从统计信息中看到的那样,正在使用查询缓存,但是没有报告二级缓存:

queries executed to database=1
query cache puts=1
query cache hits=689
query cache misses=1
....
second level cache puts=0
second level cache hits=0
second level cache misses=0
entities loaded=1
....

一个简单的手写哈希表作为缓存,按预期工作,大大减少了总时间。我想由于我的操作性质,我没有触发Hibernate的缓存。

如何在此设置中使用hibernate的二级缓存?对于记录,这是我的持久性xml:

http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd”              版本= “2.0” &GT;

<provider>org.hibernate.ejb.HibernatePersistence</provider> 
<class>...</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>

  <properties>
   <property name="hibernate.connection.driver_class" value="org.postgresql.Driver" />
    <property name="hibernate.connection.password" value="zyx" />
    <property name="hibernate.connection.url" value="jdbc:postgresql://192.168.0.194:5432/testdbforml" />
    <property name="hibernate.connection.username" value="postgres"/>
    <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/>
    <property name="hibernate.search.autoregister_listeners" value="false"/>
    <property name="hibernate.jdbc.batch_size" value="200"/>
     <property name="hibernate.connection.autocommit" value="false"/> 
     <property name="hibernate.generate_statistics" value="true"/>
    <property name="hibernate.cache.use_structured_entries" value="true"/>

    <property name="hibernate.cache.use_second_level_cache" value="true"/>
     <property name="hibernate.cache.use_query_cache" value="true"/>           

     <property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory"/>              

  </properties>

1 个答案:

答案 0 :(得分:2)

好的,我找到了。 我的问题是,缓存查询只保留缓存中的查询结果ID,并且(可能)返回到db以获取实际值,而不是从二级缓存中获取它们。

问题当然是,查询没有将这些值放到二级缓存中,因为它们没有被主ID选中。所以解决方案是使用一个将值放到二级缓存的方法,而使用hibernate 4.1,我已经设法用自然id做到这一点。以下是从缓存中插入或返回值的函数,以防万一它可以帮助其他任何人:

private UUID persistPath(String pPath) throws Exception{
        org.hibernate.Session session = (Session) entityManager.getDelegate();
        NodePath np = (NodePath) session.byNaturalId(NodePath.class).using("path", pPath).load();
        if(np != null)
            return np.getId();
        else {//no such path entry, so let's create one
            NodePath newPath = new NodePath();
            newPath.setPath(pPath);
            entityManager.persist(newPath);
            return newPath.getId();
        }


    }