我如何在JPA中使用Hibernate的二级缓存? [英] How do I use Hibernate's second level cache with JPA?

查看:160
本文介绍了我如何在JPA中使用Hibernate的二级缓存?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



主表具有数百万行,而不是重复路径,我已将路径标准化为自己的表。当插入到主表中时,以下是我想要的行为:1)检查路径表中是否存在路径(通过实体管理器查询,使用路径值作为参数)如果它不存在,插入和获取id(通过实体管理器持久保存)



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

对于一组域对象,这将发生数千次,这些对象与主表和其他表中的大量行对应。所以上面的步骤重复使用像这样的单个事务:

  EntityTransaction t = entityManager.getTransaction(); 
t.begin();
//执行上面给出的步骤,检查,然后坚持等等。
t.commit();

当我执行第2步时,会导致整个操作的性能下降很大。它乞求缓存,因为一段时间后,该表最多将有10-20k条目,而且非常少见的新插入。我试着用Hibernate来做到这一点,并且差不多耗时2天。

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

 查询调用= 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)
抛出新的异常(path table should have unique paths);
else if(paths.size()== 1){
NodePath path = paths.get(0);
return path.getId();
}
else {//路径为null或具有零大小
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实现java.io.Serializable {

查询缓存正在被使用,据我可以看到统计数据,但没有报告使用第二级缓存:

 查询执行到数据库= 1 
查询缓存puts = 1
查询缓存命中= 689
查询缓存未命中= 1
....
二级缓存puts = 0
二级高速缓存命中= 0
二级高速缓存未命中= 0
实体加载= 1
....

一个简单的手写散列表作为缓存,按预期工作,大幅缩短总时间。我想我由于操作的本质而无法触发Hibernate的缓存。



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



http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd
version =2.0>

 < provider> org.hibernate.ejb.HibernatePersistence< / provider> 
< class> ...< / class>
< exclude-unlisted-classes> true< / exclude-unlisted-classes>
<共享高速缓存模式> ENABLE_SELECTIVE< /共享高速缓存模式>

<属性>
< property name =hibernate.connection.driver_classvalue =org.postgresql.Driver/>
< property name =hibernate.connection.passwordvalue =zyx />
< property name =hibernate.connection.urlvalue =jdbc:postgresql://192.168.0.194:5432 / testdbforml/>
< property name =hibernate.dialectvalu E = org.hibernate.dialect.PostgreSQLDialect/>
< property name =hibernate.search.autoregister_listenersvalue =false/>
< property name =hibernate.jdbc.batch_sizevalue =200/>
< property name =hibernate.connection.autocommitvalue =false/>
< property name =hibernate.generate_statisticsvalue =true/>
< property name =hibernate.cache.use_structured_entriesvalue =true/>

< property name =hibernate.cache.use_second_level_cachevalue =true/>
< property name =hibernate.cache.use_query_cachevalue =true/>

< property name =hibernate.cache.region.factory_classvalue =org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory/>

< / properties>




解决方案

好的,我找到了。
我的问题是,缓存查询只保留查询结果的Ids在缓存中,它可能(可能)回到数据库以获取实际值,而不是从二级缓存中获取它们。

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

  private UUID persistPath( String pPath)抛出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 {//没有这样的路径入口,所以让我们创建一个
NodePath newPath = new NodePath();
newPath.setPath(pPath);
entityManager.persist(newPath);
return newPath.getId();
}


}


I am implementing an Entity Attribute Value based persistence mechanism. All DB access is done via Hibernate. I have a table that contains paths for nodes, it is extremely simple, just an id, and a path (string) The paths would be small in number, around a few thousand.

The main table has millions of rows, and rather than repeating the paths, I've normalized the paths to their own table. The following is the behaviour I want, when inserting into main table

1) Check if the path exists in paths table (query via entity manager, using path value as parameter)

2) if it does not exist, insert, and get id (persist via entity manager)

3) put id as foreign key value to main table row, and insert this into main table.

This is going to happen thousands of times for a set of domain objects, which correspond to lots of rows in main table and some other tables. So the steps above are repeated using a single transaction like this:

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

When I perform step 2, it introduces a huge performance drop to the total operation. It is begging for caching, because after a while that table will be at most 10-20k entries with very rare new inserts. I've tried to do this with Hibernate, and lost almost 2 days.

I'm using Hibernate 4.1, with JPA annotations and ECache. I've tried to enable query caching, even using the same query object throughout the inserts, as shown below:

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();
        }

The NodePath entity is annotated as follows:

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

The query cache is being used, as far as I can see from the statistics, but no use for second level cache is reported:

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
....

A simple, hand written hashtable as a cache, works as expected, cutting down total time drastically. I guess I'm failing to trigger Hibernate's caching due to nature of my operations.

How do I use hibernate's second level cache with this setup? For the record, this is my persistence xml:

http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" version="2.0">

<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>

解决方案

Ok, I found it. My problem was that, cached query was keeping only Ids of query results in the cache, and it was (probably) going back to db to get the actual values, rather than getting them from the second level cache.

The problem is of course, the query did not put those values to second level cache, since they were not selected by primary id. So the solution is to use a method that will put values to second level cache, and with hibernate 4.1, I've manage to do this with natural id. Here is the function that either inserts or returns the value from cache, just in case it helps anybody else:

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();
        }


    }

这篇关于我如何在JPA中使用Hibernate的二级缓存?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆