【问题标题】:Collections not read from hibernate/ehcache second-level-cache未从 hibernate/ehcache 二级缓存读取的集合
【发布时间】:2013-11-07 00:56:07
【问题描述】:

我正在尝试在 Spring 项目中使用 ehcache/hibernate 缓存延迟加载的集合。当我执行 session.get(Parent.class, 123) 并多次浏览子项时,每次都会执行查询以获取子项。只有第一次查询父节点,然后从缓存中解析。

可能我遗漏了一些东西,但我找不到解决方案。请看下面的相关代码。

我正在使用 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>

我正在运行的测试用例:

@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(具有不同的并发级别)。不幸的是,我们一直遇到同样的问题。

附:这个问题在 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

【问题讨论】:

  • 你的测试使用的 sessionFactory 是由 Spring 管理的,我想?
  • 是的 sessionFactory 是弹簧管理的。
  • 只是为了确保延迟加载不会影响问题,您是否尝试过相同的测试,但在集合上将获取类型设置为 fetch=FetchType.EAGER
  • 是的,我是否也尝试过渴望但没有任何成功。

标签: java spring hibernate ehcache second-level-cache


【解决方案1】:

这里的问题在于您的 Hibernate 版本。下面的代码将解释这里出了什么问题。

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 票证的链接。解决方案是启用跟踪日志记录(糟糕——忘了我什至说过这个)或将您的库升级到 4.2.2 或更高版本。 https://hibernate.atlassian.net/browse/HHH-8250

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-12-12
    • 2014-11-10
    • 2010-10-28
    • 1970-01-01
    • 1970-01-01
    • 2012-06-27
    • 1970-01-01
    • 2016-10-08
    相关资源
    最近更新 更多