标准渴望fetch-joined集合以避免n + 1选择

时间:2012-10-08 21:59:12

标签: java hibernate hibernate-criteria

假设Item和Bid是实体:Item有很多Bids。它们在典型的父/子关系中以 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();                        

4 个答案:

答案 0 :(得分:10)

此条件查询似乎正确:

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

答案 1 :(得分:6)

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

  

“如果表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

答案 2 :(得分:1)

我认为您需要的是在另一个Criteria上使用Subquery Exists的Criteria。类似的答案在这里: 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"))));

答案 3 :(得分:0)

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

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