2012-05-15 49 views
7

我正在實現基於實體屬性值的持久性機制。所有數據庫訪問都通過Hibernate完成。 我有一個包含節點路徑的表,它非常簡單,只是一個id和一個路徑(字符串)路徑數量很少,大約有幾千個。如何在JPA中使用Hibernate的二級緩存?

主表具有數百萬行,而不是重複路徑,我已將路徑標準化爲自己的表。以下是我想,在插入時進入主表

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

一個簡單的,手寫的哈希表高速緩存,按預期工作,切割d自己的總時間很大。我想我由於操作的本質而無法觸發Hibernate的緩存。

如何在此設置中使用hibernate的二級緩存?根據記錄,這是我的堅持XML:

http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd」 版本= 「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> 

+0

非常有趣!感謝您的問題和答案;)+1 – MychaL

回答

2

好吧,我找到了它 我的問題是,緩存查詢只保留查詢結果的Ids在緩存中,它(可能)回到數據庫獲取實際值,而不是獲取它們來自二級緩存。

問題當然是,查詢沒有將這些值放到二級緩存中,因爲它們沒有被主ID選中。所以解決方案是使用一個方法,將值放到二級緩存,並與休眠4。1,我已經設法用自然標識來做到這一點。這裏是插入或從緩存返回值的功能,以防萬一它幫助其他人:

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


    } 
相關問題