假设我有两个表,Books和Reviews。 Reviews有一个列stars,可以有1到5的值。一本书可以有多个评论。
如何使用Criteria API选择所有书籍,使得每本书只返回其前3篇和后3篇评论(而不是所有评论)?
如果Criteria API无法实现,我可以尝试其他建议,例如HQL、SQL等。
criteria.addOrder(Order.desc("id"));
criteria.setMaxResults(1);
不确定它在效率方面的表现如何。我假设它是在执行Select *
之后进行过滤的?
// you should have the DAO class containing this queries
// extend HibernateDaoSupport
List<Tag> topList = getSession().createQuery("FROM Reviews r WHERE r.book = "
+ book + " ORDER BY r.stars asc").setMaxResults(3).list();
List<Tag> bottomList = getSession().createQuery("FROM Reviews r WHERE r.book = "
+ book + " ORDER BY r.stars desc").setMaxResults(3).list();
回答自己的问题...
我很惊讶这有多么困难。所有涉及查询图书和每本书都有其顶部和底部评论列表的解决方案在某些数据库上会导致每本书N次查询,在我的情况下(MySql)需要3N次查询。当然,有人可能能够找到聪明的方法绕过这个问题,但是通过玩弄代码、原始SQL、研究他人所做的事情、数据库限制等等,似乎总是回到那个点。
所以...我最终将问题颠倒了过来。我并不是在查询书籍时计算顶部和底部评论(这是一个非常频繁的查询 - FYI Books/Reviews仅用于举例,实际域名不同),而是在提交新评论时计算顶部和底部评论。这将把提交评论从一个“查询”转变为三个查询,但相比查询书籍,提交评论是相对不频繁的。
为此,我向Book添加了两个新属性,topReviews和bottomReviews,并将它们映射为Review上的一对多列表。然后,我更新了保存新评论的代码,以查询有关问题的书籍的顶部和底部评论(2个查询),设置书籍的顶部/底部评论属性(覆盖以前的属性),并保存该书籍。任何书籍查询现在都返回这些“缓存”的顶部和底部评论。我仍然有一个懒惰的“reviews”属性,如果需要,可以拥有所有评论(而不仅仅是顶部/底部) - 另一个一对多映射。
我愿意听取其他建议。这里的大多数答案都接近问题,但由于数据库限制或需要太多查询而无法解决问题。
我认为你不能使用单个查询来实现你想要的功能。但是,你可以使用两个查询来实现(使用带有命名参数的 EJBQL):
SELECT r FROM Reviews r WHERE r.book = :book ORDER BY r.stars ASC LIMIT 3;
SELECT r FROM Reviews r WHERE r.book = :book ORDER BY r.stars DESC LIMIT 3;
当然,您可以编写一个简单的帮助方法,使这两个调用并将结果汇总到您喜欢的任何数据结构中。
setMaxResults
。 - Alex Gitelman@Transient
方法,该方法在支持集合上执行所需的过滤,然后返回结果(在新的集合实例中)。 - aroth这通常可以使用类似于 union 的东西来完成,但在 HQL 中无法实现。
不过,在 HQL 中你可以这样做。
WHERE id in ([SELECT top 3]) or id in ([SELECT bottom 3])
我没有测试这个的方式,但这个方法也许可行。
DetachedCriteria topN = [ your criteria ]
DetachedCriteria bottomN = [ your criteria ]
session.createCriteria(..._
.add( Property.or(Property.forName("id").in(topN),Property.forName("id").in(bottomN) )
.list();
使用 org.springframework.data.domain.Pageable 的两个 HQL 查询:
在中间层服务中,您可以创建 pageable 并调用 repo:
Pageable pageableTop = new PageRequest(0, 3, Direction.ASC, "yourFieldToSortBy");
yourRepository.findTopOrderByYourEntity(pageableTop);
Pageable pageableBottom = new PageRequest(0, 3, Direction.DESC, "yourFieldToSortBy");
yourRepository.findTopOrderByYourEntity(pageableBottom);
代码库:
public interface YourRepository extends CrudRepository<YourEntity, String> {
@Query("FROM YourEntity")
List<YourEntity> findTopOrderByYourEntity(Pageable pageable);
}