简体   繁体   English

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

[英]How do I use Hibernate's second level cache with JPA?

I am implementing an Entity Attribute Value based persistence mechanism. 我正在实现一个基于实体属性值的持久性机制。 All DB access is done via Hibernate. 所有数据库访问都是通过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. 我有一个包含节点路径的表,它非常简单,只是一个id,一个路径(字符串)路径数量很少,大约有几千个。

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) 1)检查路径表中是否存在路径(通过实体管理器查询,使用路径值作为参数)

2) if it does not exist, insert, and get id (persist via entity manager) 2)如果它不存在,插入并获取id(通过实体管理器持久化)

3) put id as foreign key value to main table row, and insert this into main table. 3)将id作为外键值放入主表行,并将其插入主表中。

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. 当我执行第2步时,它会给整个操作带来巨大的性能下降。 It is begging for caching, because after a while that table will be at most 10-20k entries with very rare new inserts. 它正在乞求缓存,因为过了一段时间,该表最多只有10-20k条目,非常罕见的新插入。 I've tried to do this with Hibernate, and lost almost 2 days. 我试图用Hibernate做到这一点,并且失去了将近2天。

I'm using Hibernate 4.1, with JPA annotations and ECache. 我正在使用Hibernate 4.1,带有JPA注释和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: NodePath实体注释如下:

@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. 我想由于我的操作性质,我没有触发Hibernate的缓存。

How do I use hibernate's second level cache with this setup? 如何在此设置中使用hibernate的二级缓存? For the record, this is my persistence xml: 为了记录,这是我的持久性xml:

http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" version="2.0"> 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. 我的问题是,缓存查询只保留缓存中的查询结果ID,并且(可能)返回到db以获取实际值,而不是从二级缓存中获取它们。

The problem is of course, the query did not put those values to second level cache, since they were not selected by primary id. 问题当然是,查询没有将这些值放到二级缓存中,因为它们没有被主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. 所以解决方案是使用一个将值放到二级缓存的方法,而使用hibernate 4.1,我已经设法用自然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();
        }


    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何创建JPA二级缓存以与实体的辅助键一起使用? - How do I create a JPA second-level cache for use with an entity's secondary key? 何时以及如何使用 hibernate 二级缓存? - When and how to use hibernate second level cache? Infinispan:用于休眠的远程二级缓存 (JPA) - Infinispan : Remote Second Level Cache for Hibernate (JPA) Ehcache二级缓存无法使用JPA和Hibernate? - Ehcache second level cache not working with JPA and Hibernate? 如何在Hibernate中将二级缓存用于延迟加载的集合? - How to use second level cache for lazy loaded collections in Hibernate? 如何为对象和集合使用hibernate二级缓存 - how to use hibernate second level cache for objects and collections 如何正确使用休眠缓存(警告已弃用适用于休眠的Ehcache二级缓存提供程序) - How properly use hibernate cache (warning The Ehcache second-level cache provider for Hibernate is deprecated) 在JBoss 4.2上使用JPA启用Hibernate二级缓存 - Enabling Hibernate second-level cache with JPA on JBoss 4.2 spring data jpa - 没有@QueryHints的休眠二级缓存 - spring data jpa - hibernate second level cache without @QueryHints 为什么我们必须使用@Cacheable 以及Hibernate 中的@Cache 作为二级缓存? - Why do we have to use @Cacheable as well as @Cache in Hibernate for second-level of cache?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM