2013-10-28 2 views
2

Я пытаюсь кэшировать ленивые загруженные коллекции с помощью ehcache/hibernate в проекте Spring. Когда я выполняю session.get (Parent.class, 123) и просматриваю дочерние элементы несколько раз, каждый раз запрос выполняется для извлечения дочерних элементов. Родитель запрашивается только в первый раз, а затем разрешается из кеша.Коллекции, которые не читаются из hibernate/ehcache второго уровня-кеша

Возможно, что-то у меня отсутствует, но я не могу найти решение. См. Соответствующий код ниже.

Я использую Spring (3.2.4.RELEASE) Hibernate (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> 

TestCase Я бег:

@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

Сессия, которую использует ваш тест, управляется весной, я полагаю? – aquaraga

+0

Да, sessionFactory управляется весной. –

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

Это ссылка на билет JIRA Hibernate относительно этого. Решение состоит в том, чтобы либо включить ведение журнала трассировки (yuck - забудьте, я даже сказал это), либо обновил ваши библиотеки до версии 4.2.2 или выше. https://hibernate.atlassian.net/browse/HHH-8250

Смежные вопросы