将预先提取加入的集合作为条件,以避免 n+1 个选择

2022-09-04 02:11:47

假设 Item 和 Bid 是实体:一个项目有许多 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.amount > 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();                        

答案 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+1default_batch_fetch_sizebatch-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

这是对为什么在提取连接的集合上添加限制会导致集合未初始化的原因(请注意,没有限制的同一查询会为集合生成预先提取):

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

但是,使用 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();

这是一个相关的帖子:休眠查询不返回完整的对象


推荐