【发布时间】:2011-07-31 05:08:20
【问题描述】:
假设我有两张表,书籍和评论。评论有一个星号列,其值可以在 1 到 5 之间。一本书可以有很多评论。
如何选择所有图书,以便使用 Criteria API 仅返回每本书的顶部和底部 3 条评论(而不是所有评论)?
如果 Criteria API 不支持,我愿意接受 HQL、SQL 等其他建议。
【问题讨论】:
假设我有两张表,书籍和评论。评论有一个星号列,其值可以在 1 到 5 之间。一本书可以有很多评论。
如何选择所有图书,以便使用 Criteria API 仅返回每本书的顶部和底部 3 条评论(而不是所有评论)?
如果 Criteria API 不支持,我愿意接受 HQL、SQL 等其他建议。
【问题讨论】:
你总是可以设置顺序 asc/desc 然后限制结果。
例如:
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、研究其他人所做的事情、数据库限制等似乎总是会回到那个问题上。
所以...我最终把问题反过来了。每当我查询书籍时(这是一个非常频繁的查询 - 仅供参考的书籍/评论是出于示例目的 - 实际域不同),我不会在每次提交新评论时计算顶部/底部评论,而不是计算顶部和底部评论。这会将提交评论从一个“查询”更改为三个查询,但与查询图书相比,提交评论的频率相当低。
为此,我向 Book 添加了两个新属性 topReviews 和 bottomReviews 并将它们映射为 Review 上的一对多列表。然后,我更新了保存新评论的代码,以查询相关图书的顶部和底部评论(2 个查询),设置图书的顶部/底部评论属性(覆盖前一个),并保存图书。现在,任何图书查询都会返回这些“缓存”的顶部和底部评论。如果需要,我仍然有一个惰性“评论”属性,它将包含所有评论(不仅仅是顶部/底部) - 另一个一对多映射。
我愿意接受其他建议。这里的大多数答案都在球场上,但由于数据库限制或需要太多查询而错过了问题或无法工作。
【讨论】:
我认为你不能使用单个查询来做你想做的事。但是,您当然可以分两步完成(使用带有命名参数的 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。
@Transient 方法,该方法对支持集合执行所需的过滤,然后返回结果(在新集合中实例)。
这通常使用联合之类的方法来完成,而在 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();
【讨论】:
如何将 Review 和 Book 映射为多对多,然后在 Book 的评论集合中指定:order-by="rating desc" 和 lazy="true"
假设使用延迟获取方法,仅当要从集合中获取对象时才会获取数据,但我不确定这一点。要确认,您可以打开 Hibernate SQL 日志记录并监控在运行时进行了哪些查询。
【讨论】:
使用 org.springframework.data.domain.Pageable 的两个 HQL 查询,例如:
在中间层服务中创建可分页并调用 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);
}
【讨论】: