【问题标题】:Criteria eager fetch-joined collection to avoid n+1 selectsCriteria Eager fetch-joined 集合以避免 n+1 选择
【发布时间】:2012-09-29 04:43:36
【问题描述】:

假设 Item 和 Bid 是实体:一个 Item 有多个 Bid。它们在 Hibernate 中以典型的父/子关系映射:

<class name="Item" table="ITEM">
  ...
  <set name="bids" inverse="true">
    <key column="ITEM_ID"/>
    <one-to-many class="Bid"/>
  </set>
</class>

在执行此查询后尝试访问每个项目的出价时,如何避免 n+1 选择?

List<Item> items = session.createCriteria(Item.class)
                        .createAlias("bids", "b").
                        .add(Restrictions.gt("b.amount", 100)).
                        .list();

注意我需要急切地获取出价,但对收藏有进一步的限制(b.数量 > 100)

我尝试了以下失败:

List<Item> items = session.createCriteria(Item.class)
                        .setFetchMode("bids", FetchMode.JOIN).
                        .createAlias("bids", "b").
                        .add(Restrictions.gt("b.amount", 100)).
                        .list();                        

List<Item> items = session.createCriteria(Item.class)
                        .createCriteria("bids")
                        .add(Restrictions.gt("amount", 100)).
                        .list();                        

【问题讨论】:

标签: java hibernate hibernate-criteria


【解决方案1】:

这个条件查询似乎是正确的:

  List<Item> items = session.createCriteria(Item.class)
                    .setFetchMode("bids", FetchMode.JOIN)
                    .createAlias("bids", "b")
                    .add(Restrictions.gt("b.amount", 100))
                    .list();

FetchMode.JOIN 旨在解决n+1 问题。你定义了一些default_batch_fetch_size | batch-size 映射或配置中的任何位置,这会产生反向影响吗?

如果没有,你能不能试试下面的 HQL 看看这能解决你的问题吗?

 Query query = 
      session.createQuery("from Item it left join it.bids b where b.amount=:bids");
 query.setParamter(bids, 100);
 List<Item> items = query.list();

【讨论】:

    【解决方案2】:

    这解释了为什么在 fetch-joined 集合上添加限制会导致集合未初始化(注意没有限制的相同查询会产生对集合的急切获取):

    “如果您在表 A 和 B 之间存在 1:n 关系,并且您向 B 添加了一个限制并且想要急切地获取 A 和 B,那么问题是当您想要从 A 导航到 B 时会发生什么. 您应该只看到 B 中与限制匹配的数据,还是应该看到与 A 相关的所有 B?”see more here ...

    但是,使用 HQL 而不是标准,获取部分投标集合

    List<Item> items = session.createQuery(
              "from Item i left join fetch i.bids b where b.amount > :amount")
              .setParameter("amount", 100)
              .list();
    

    在我看来是不一致的,但这就是它的工作原理

    顺便说一句,如果你需要的是父母及其所有孩子的列表,但只是孩子都符合一定限制的父母,那么你可以使用这个

    List<Item> items = session.createQuery(
              "from Item i left join fetch i.bids b " +
              "where not exists (from Bid b where b.item = i and b.amount <= :amount)")
              .setParameter("amount", 100)
              .list();
    

    这是一个相关的帖子:Hibernate query not returning full object

    【讨论】:

      【解决方案3】:

      我认为您需要的是在另一个条件上使用子查询的条件。类似的答案在这里: https://stackoverflow.com/a/15768089/1469525

      DetachedCriteria criteria = session.createCriteria(Item.class, "i");
      criteria.setFetchMode("bids", FetchMode.JOIN);
      
      DetachedCriteria bidCriteria = DetachedCriteria.forClass(Bid.class, "b");
      bidCriteria.add(Restrictions.gt("amount", 100));
      bidCriteria.add(Restrictions.eqProperty("b.itemId", "i.id"));
      criteria.add(Subqueries.exists(bidCriteria.setProjection(Projections.property("b.id"))));
      

      【讨论】:

        【解决方案4】:

        试试这个代码。它解决了我的问题。

        List<Item> items = session.createCriteria(Item.class)
                        .setFetchMode("bids", FetchMode.SELECT)
                        .createAlias("bids", "b")
                        .add(Restrictions.gt("b.amount", 100))
                        .list();
        

        【讨论】:

          猜你喜欢
          • 2011-08-03
          • 2015-11-28
          • 2012-05-19
          • 2011-12-07
          • 2011-07-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-11-02
          相关资源
          最近更新 更多