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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10600698/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 01:47:31  来源:igfitidea点击:

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

javahibernatecachingjpanhibernate-caches

提问by mahonya

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.

我正在实施基于实体属性值的持久性机制。所有数据库访问都是通过 Hibernate 完成的。我有一个包含节点路径的表,它非常简单,只有一个 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. 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.

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

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:

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

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

回答by mahonya

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

问题当然是,查询没有将这些值放入二级缓存,因为它们不是由主 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();
        }


    }