2013-10-28 51 views
2

我試圖緩存延遲加載的集合與ehcache /休眠在Spring項目。當我執行session.get(Parent.class,123)並多次瀏覽孩子時,每次執行一次查詢以獲取孩子。只有第一次查詢父級,然後從緩存中解析父級。集合不讀取從休眠/ ehcache二級緩存

可能我錯過了一些東西,但找不到解決方案。請參閱下面的相關代碼。

我使用Spring(3.2.4.RELEASE)休眠(4.2.1.Final)和的Ehcache(2.6.6)

的父類:

@Entity 
@Table(name = "PARENT") 
@Cacheable 
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE, include = "all") 

public class Parent implements Serializable { 
/** The Id. */ 
    @Id 
    @Column(name = "ID") 
    private int id; 


    @OneToMany(fetch = FetchType.LAZY, mappedBy = "parent") 
    @Cache(usage = CacheConcurrencyStrategy.READ_WRITE) 
    private List<Child> children; 

    public List<Child> getChildren() { 
     return children; 
    } 

    public void setChildren(List<Child> children) { 
     this.children = children; 
    } 

    @Override 
    public boolean equals(Object o) { 
     if (this == o) return true; 
     if (o == null || getClass() != o.getClass()) return false; 
     Parent that = (Parent) o; 
     if (id != that.id) return false; 
     return true; 
    } 

    @Override 
    public int hashCode() { 
     return id; 
    } 
} 

的子類:

@Entity 
@Table(name = "CHILD") 
@Cacheable 
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE, include = "all") 
public class Child { 

    @Id 
    @Column(name = "ID") 
    private int id; 

    @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) 
    @JoinColumn(name = "PARENT_ID") 
    @Cache(usage = CacheConcurrencyStrategy.READ_WRITE) 
    private Parent parent; 

    public int getId() { 
    return id; 
    } 

    public void setId(final int id) { 
    this.id = id; 
    } 

    private Parent getParent(){ 
     return parent; 
    } 

    private void setParent(Parent parent) { 
     this.parent = parent; 
    } 

    @Override 
    public boolean equals(final Object o) { 
     if (this == o) { 
      return true; 
     } 
     if (o == null || getClass() != o.getClass()) { 
      return false; 
    } 
     final Child that = (Child) o; 
     return id == that.id; 
    } 

    @Override 
    public int hashCode() { 
     return id; 
    } 
} 

應用程序上下文:

<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"> 
    <property name="dataSource" ref="dataSource" /> 
    <property name="annotatedClasses"> 
     <list> 
      <value>Parent</value> 
      <value>Child</value> 
     </list> 
    </property> 

    <property name="hibernateProperties"> 
     <props> 
      <prop key="hibernate.dialect">org.hibernate.dialect.SQLServer2008Dialect</prop> 
      <prop key="hibernate.hbm2ddl.auto">validate</prop> 
      <prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop> 
      <prop key="hibernate.connection.charSet">UTF-8</prop> 
      <prop key="hibernate.show_sql">true</prop> 
      <prop key="hibernate.format_sql">true</prop> 
      <prop key="hibernate.use_sql_comments">true</prop> 

      <!-- cache settings ehcache--> 
      <prop key="hibernate.cache.use_second_level_cache">true</prop> 
      <prop key="hibernate.cache.use_query_cache">true</prop> 
      <prop key="hibernate.cache.region.factory_class"> org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory</prop> 
      <prop key="hibernate.generate_statistics">true</prop> 
      <prop key="hibernate.cache.use_structured_entries">true</prop> 
      <prop key="hibernate.cache.use_query_cache">true</prop> 
      <prop key="hibernate.transaction.factory_class"> org.hibernate.engine.transaction.internal.jta.JtaTransactionFactory</prop> 
      <prop key="hibernate.transaction.jta.platform"> org.hibernate.service.jta.platform.internal.JBossStandAloneJtaPlatform</prop> 
     </props> 
    </property> 
</bean> 

我運行測試用例:

@Test 
public void testGetParentFromCache() { 
    for (int i = 0; i <3 ; i++) { 
     getEntity(); 
    } 

} 

private void getEntity() { 
    Session sess = sessionFactory.openSession() 
    sess.setCacheMode(CacheMode.NORMAL); 
    Transaction t = sess.beginTransaction(); 

    Parent p = (Parent) s.get(Parent.class, 123); 
    Assert.assertNotNull(p); 
    Assert.assertNotNull(p.getChildren().size()); 
    t.commit(); 
    sess.flush(); 
    sess.clear(); 
    sess.close(); 
} 

在記錄我可以看到,第一次2個查詢執行獲取父和獲取孩子。此外,日誌顯示,子實體以及集合都存儲在二級緩存中。但是,當讀取集合時,會執行查詢以在第二次和第三次嘗試時取出孩子。

由於EHCache不起作用,我們也嘗試了infinispan(具有不同的併發級別)。不幸的是,我們仍然遇到同樣的問題。

P.S.這個問題也解決在的EHCache論壇:http://forums.terracotta.org/forums/posts/list/8785.page 和休眠論壇:https://forum.hibernate.org/viewtopic.php?f=1&t=1029899

而且我的同事創建了一個例子項目在GitHub上類似於我們的項目設置和問題:https://github.com/basvanstratum/cacheimpl

+0

您測試使用的sessionFactory是由Spring管理的,我想呢? – aquaraga

+0

是的sessionFactory是spring管理的。 –

+0

爲了確保延遲加載不會影響問題,您是否嘗試過相同的測試,但在集合上將獲取類型設置爲'fetch = FetchType.EAGER'? –

回答

2

這裏的問題在於你的版本中的休眠。下面的代碼將解釋這裏發生了什麼問題。

public class DefaultInitializeCollectionEventListener 
     implements InitializeCollectionEventListener { 
    public void onInitializeCollection(InitializeCollectionEvent event) 
     throws HibernateException { 
    … 
    final boolean traceEnabled = LOG.isTraceEnabled(); 
    … 

    final boolean foundInCache = methodReturningTrueWhenInCache(); 

    if (foundInCache && traceEnabled) { 
     LOG.trace("Collection initialized from cache"); 
    } 
    else { 
     if (traceEnabled) { 
     LOG.trace("Collection not cached"); 
     } 
     methodThatExecutesAQuery(); 
    } 
} 

這是Hibernate的JIRA票據的鏈接。解決方案是啓用跟蹤日誌記錄(yuck - 忘記我甚至說過)或者將庫升級到4.2.2或更高版本。 https://hibernate.atlassian.net/browse/HHH-8250