【问题标题】:Hibernate second level cache problemsHibernate二级缓存问题
【发布时间】:2011-07-22 11:30:44
【问题描述】:

我有多对一的关系,假设用户有一家公司。公司实体没有任何其他关系。

如果我为 Company 的 findAll hibernate 查询启用二级缓存,我第二次重新加载页面(这意味着加载了用户,然后还加载了所有公司的列表)我会为每个现有公司选择(选择 .. . from company where id=?) 在休眠输出中。 当为 Company 调用 findAll 时会发生这种情况,它看起来像这样(这是类中的泛型方法,使用适当的类型进行了扩展):

return (List<T>) getHibernateTemplate().execute(new HibernateCallback() {

            public Object doInHibernate(Session session)
                    throws HibernateException, SQLException {
                Criteria c = session.createCriteria(persistentClass);
                c.setCacheable(cacheFindAll);

                return c.list();
            }
        });

我在只读策略中使用 Jboss TreeCacheProvider。如果我使用setCacheable(false),则没有“不需要的”选择。

为什么会发生这种情况,我该如何消除这种行为?

【问题讨论】:

    标签: java hibernate caching


    【解决方案1】:

    调用setCacheable()是为了启用查询缓存,见javadoc

    Enable caching of this query result, provided query caching is enabled for the underlying session

    发生的情况是公司的 ID 被缓存,而不是对象本身。如果您启用二级缓存,这些单独的公司查询将由二级缓存处理。

    为了启用二级缓存,您需要在 persistence.xml 中设置 hibernate.cache.use_second_level_cache=true。此外,您还需要注释您想要缓存在二级缓存中的关系和实体。

    @OneToMany
    @org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
    private List<Company> companies;
    

    以及实体缓存:

    @Entity
    @org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
    public class Company {
        ...
    }
    

    顺便说一句,二级缓存只会缓存您在 id 上找到的关系和实体。查询不能被二级缓存缓存,会一直到数据库(或查询缓存)。

    【讨论】:

    • 谢谢,是的,你是对的 setCacheable 设置查询缓存。但是我仍然不明白为什么会发生多个选择。它们仅在 setCacheable(true) 和二级缓存设置为只读时发生,当我将其设置为事务时它不会发生。我也尝试过读写 EHcache,但没有发生。另外,有没有办法缓存我需要全部加载的实体? (如国家名单等)
    • 如果您执行 EntityManager.createQuery("from Company").getResultList();您将在二级缓存中拥有所有公司。然后,如果您确保已将所有关系与公司延迟加载,并且您没有在查询中引用公司,那么它们将始终从二级缓存中读取(也就是说,如果您不让缓存过期或溢出)。