【问题标题】:Hibernate count collection size without initializing休眠计数集合大小而不初始化
【发布时间】:2011-02-24 04:25:24
【问题描述】:

有没有一种方法可以在不初始化的情况下计算关联集合的大小?

例如

Select count(p.children) from Parent p

(我不能以任何其他方式这样做是有充分理由的,因为我的 where 子句更复杂,而我的 from 子句是一个多态查询)

谢谢。

【问题讨论】:

  • 请注意,在使用 LazyCollection 上的包含进行存在性检查时,您似乎无法控制所使用的密钥。这有点棘手,因为您不能使用自然键来进行存在性检查。

标签: java hibernate lazy-loading


【解决方案1】:

查询以外的可能解决方案可能是将children 映射到lazy="extra"(以XML 表示法)。这样,您可以使用所需的任何查询来获取 Parent,然后调用 parent.getChildren().size() 而无需加载整个集合(仅执行 SELECT COUNT 类型的查询)。

有了注释,就可以了

@OneToMany
@org.hibernate.annotations.LazyCollection(
org.hibernate.annotations.LazyCollectionOption.EXTRA
)
private Set<Child> children = new HashSet<Child>();

更新:引用Java Persistence with Hibernate,ch。 13.1.3:

如果您调用任何不是标识符 getter 的方法,则会初始化代理 方法,如果您开始遍历其元素,或者如果 您调用任何集合管理操作,例如size()contains()。 Hibernate 提供了一个额外的设置,它对大型集合非常有用;它们可以被映射为额外的懒惰。 [...]

[如上映射,] 如果您调用size()contains()isEmpty(),则不再初始化集合 — 将查询数据库以检索必要的信息。如果是MapList,则操作containsKey()get()也可以直接查询数据库。

因此,使用如上映射的实体,您可以这样做

Parent p = // execute query to load desired parent
// due to lazy loading, at this point p.children is a proxy object
int count = p.getChildren().size(); // the collection is not loaded, only its size

【讨论】:

  • 你能详细说明一下吗?
  • 如果只需要 isEmpty(),Hibernate 不应运行 COUNT(*) 查询。另见:blog.jooq.org/2016/09/14/…
  • COUNT 被执行是因为 Hibernate 缓存了集合大小,因此 collection.isEmpty()collection.size() 都使用 cachedSize 而不是总是执行 COUNT(*)。但是,您对 collection.isEmpty() 的看法是正确的,它可以使用 EXISTS 代替。但是,EXTRA_LAZY 也不是真正的性能优化(我认为它更像是代码异味),因为如果您有一个非常大的集合,最好避免使用集合而只使用分页查询。跨度>
  • @VladMihalcea:我不确定我是否跟随。这与分页无关,仅与子记录的存在有关。我认为这是一个很常见的用例,不是吗?
  • 添加了 LazyCollectionOption.EXTRA,这样您即使在导航时也不必完全加载集合。因此,它不是全部加载,而是像游标一样一个接一个地加载每个元素。如果您只需要几条记录,您将不会看到任何显着的性能下降。但是如果你加载很多项目,性能会很差。
【解决方案2】:

您可以使用 Session#createFilter,它是一种 HQL 形式,可显式操作集合。例如,您提到 Parent 和 Children,所以如果您有 Person p,最基本的形式是:

session.createFilter( p.getChildren(), "" ).list()

这只是返回一个孩子的列表。重要的是要注意返回的集合不是“活的”,它与 p 没有任何关联。

有趣的部分来自第二个论点。这是一个 HQL 片段。例如,您可能希望:

session.createFilter( p.getChildren(), "select count(*)" ).uniqueResult();

你提到你有一个 where 子句,所以你也可能想要:

session.createFilter( p.getChildren(), "select count(*) where this.age > 18" ).uniqueResult();

请注意,没有 from 子句。也就是说,from 子句是从关联中隐含的。集合的元素被赋予别名“this”,因此您可以从 HQL 片段的其他部分引用它。

【讨论】:

    【解决方案3】:

    你也可以这样做:

    @Override
    public FaqQuestions getFaqQuestionById(Long questionId) {
        session = sessionFactory.openSession();
        tx = session.beginTransaction();
        FaqQuestions faqQuestions = null;
        try {
            faqQuestions = (FaqQuestions) session.get(FaqQuestions.class,
                    questionId);
            Hibernate.initialize(faqQuestions.getFaqAnswers());
    
            tx.commit();
            faqQuestions.getFaqAnswers().size();
        } finally {
            session.close();
        }
        return faqQuestions;
    }
    

    只需在您的控制器中使用 faqQuestions.getFaqAnswers().size(),您将获得延迟初始化列表的大小,而无需获取列表本身。

    【讨论】:

    • 这里到底发生了什么以及为什么它应该起作用?为什么要提交事务?
    猜你喜欢
    • 1970-01-01
    • 2013-09-14
    • 2013-06-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多