5

implementation-result-paging-in-hibernate-getting-total-number-of-rows问题引发了我的另一个问题,关于一些实现问题

现在您知道必须重用部分 HQL 查询来进行计数,如何有效地重用?

两个 HQL 查询的区别是:

  1. 选择是count(?),而不是 pojo 或属性(或列表)
  2. 提取不应发生,因此不应连接某些表
  3. 应该order by消失

还有其他区别吗?

您是否有编码最佳实践来有效地实现这种重用(关注点:工作量、清晰度、性能)?

一个简单的 HQL 查询示例:

    select       a     from A a join fetch a.b b where a.id=66 order by a.name
    select count(a.id) from A a                  where a.id=66

更新

我收到了以下答复:

  • 使用标准(但我们主要使用 HQL)
  • 操作字符串查询(但每个人都同意它看起来很复杂而且不是很安全)
  • 包装查询,依赖数据库优化(但有一种感觉,这样不安全)

我希望有人会沿着另一条路径提供选项,与字符串连接更相关。
我们可以使用公共部分构建两个 HQL 查询吗?

4

4 回答 4

3

您是否尝试通过在您的(SQL?)标准上设置投影来让 Hibernate 清楚您的意图?我大部分时间都在使用标准,所以我不确定这对你的情况有多适用,但我一直在使用

getSession().createCriteria(persistentClass).
setProjection(Projections.rowCount()).uniqueResult()

并让 Hibernate 自己找出缓存/重用/智能的东西.. 不太确定它实际上做了多少智能的东西.. 有人愿意对此发表评论吗?

于 2009-10-21T13:47:18.533 回答
2

好吧,我不确定这是最佳实践,但这是我的实践:)

如果我有类似的查询:

select A.f1,A.f2,A.f3 from A, B where A.f2=B.f2 order by A.f1, B.f3

我只想知道会得到多少结果,我执行:

select count(*) from ( select A.f1, ... order by A.f1, B.f3 )

然后将结果作为 Integer 获取,而不将结果映射到 POJO。

解析您的查询以删除某些部分,例如“order by”非常复杂。一个好的 RDBMS 将为您优化查询。

好问题。

于 2009-10-21T13:00:40.737 回答
1

好问题。这是我过去所做的(你已经提到了很多事情):

  1. 检查是否存在SELECT子句。
    1. 如果不是,请添加select count(*)
    2. 否则检查它是否有DISTINCT或聚合函数。如果您使用 ANTLR 来解析您的查询,则可以解决这些问题,但它非常复杂。你最好用select count(*) from ().
  2. 消除fetch all properties
  3. fetch如果您将 HQL 解析为字符串,请从连接中删除。如果你真的用 ANTLR 解析查询,你可以left join完全删除;检查所有可能的参考资料相当麻烦。
  4. 消除order by
  5. 根据您在 1.2 中所做的事情,您需要删除 / 调整group by/ having

以上自然适用于 HQL。对于 Criteria 查询,您可以做的事情非常有限,因为它不适合轻松操作。如果您在 Criteria 之上使用某种包装层,您最终将得到相当于(有限)ANTLR 解析结果子集的结果,并且在这种情况下可以应用上述大部分内容。

由于您通常会保留当前页面的偏移量和总计数,因此我通常首先使用给定的限制/偏移量运行实际查询,并且仅在count(*)返回的结果数大于或等于限制且偏移量为零时才运行查询(在所有其他情况下,我要么运行count(*)之前,要么无论如何我都得到了所有结果)。当然,对于并发修改,这是一种乐观的方法。

更新(关于手工组装 HQL)

我不是特别喜欢这种方法。当映射为命名查询时,HQL 具有构建时错误检查的优势(好吧,从技术上讲,是运行时,因为必须构建 SessionFactory,尽管这通常在集成测试期间完成)。在运行时生成时,它会在运行时失败:-) 进行性能优化也不是一件容易的事。

当然,同样的推理也适用于 Criteria,但由于定义明确的 API 而不是字符串连接,因此更难搞砸。并行构建两个 HQL 查询(一个分页和一个“全局计数”一个)也会导致代码重复(并且可能会导致更多错误),或者迫使您在顶部编写某种包装层来为您执行此操作。这两种方式都远非理想。如果您需要从客户端代码(如通过 API)执行此操作,问题会变得更糟。

在这个问题上,我其实已经思考了很多。来自Hibernate-Generic-DAO 的搜索 API似乎是一个合理的折衷方案;我对上述链接问题的回答中有更多细节。

于 2009-10-21T17:25:03.523 回答
0

在徒手 HQL 情况下,我会使用类似的东西,但这不可重用,因为它对于给定的实体非常具体

Integer count = (Integer) session.createQuery("select count(*) from ....").uniqueResult();

这样做一次并相应地调整起始编号,直到您翻页。

对于标准,虽然我使用这样的样本

final Criteria criteria = session.createCriteria(clazz);  
            List<Criterion> restrictions = factory.assemble(command.getFilter());
            for (Criterion restriction : restrictions)
                criteria.add(restriction);
            criteria.add(Restrictions.conjunction());
            if(this.projections != null)
                criteria.setProjection(factory.loadProjections(this.projections));
            criteria.addOrder(command.getDir().equals("ASC")?Order.asc(command.getSort()):Order.desc(command.getSort()));
            ScrollableResults scrollable = criteria.scroll(ScrollMode.SCROLL_INSENSITIVE);
            if(scrollable.last()){//returns true if there is a resultset
                genericDTO.setTotalCount(scrollable.getRowNumber() + 1);
                criteria.setFirstResult(command.getStart())
                        .setMaxResults(command.getLimit());
                genericDTO.setLineItems(Collections.unmodifiableList(criteria.list()));
            }
            scrollable.close();
            return genericDTO;

但这每次都会通过调用ScrollableResults:last().

于 2009-10-21T15:48:03.593 回答