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

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

 ...      

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

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

注意我需要一个热切的提取出价,但对集合有进一步的限制 (b.amount> 100)

我尝试了下面的失败:

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

此条件查询似乎正确:

  List 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 items = query.list(); 

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

“如果表A和B之间有1:n的关系,并且你向B添加限制并且想要急切地获取A和B,那么当你想从A导航到B时会出现什么问题。你应该只看到B中符合限制的数据,或者你应该看到所有与A相关的B?“ 在这里看到更多……

但是,使用HQL而不是条件,部分获取出价集合

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

在我看来这是不一致的,但它是如何工作的

顺便说一句,如果你需要的是父母及其所有孩子的名单,但只有父母的孩子都满足一定的限制,所以你可以使用这个

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

这是一个相关post: Hibernate查询不返回完整对象 。

我认为你需要的是一个在另一个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")))); 

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

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