简体   繁体   English

重用查询的一部分进行计数的Java编码最佳实践

[英]Java coding best-practices for reusing part of a query to count

The implementing-result-paging-in-hibernate-getting-total-number-of-rows question trigger another question for me, about some implementation concern : 休眠中实现结果分页的行总数问题对我来说引发了另一个问题,涉及一些实现问题

Now you know you have to reuse part of the HQL query to do the count, how to reuse efficiently? 现在您知道必须重用HQL查询的一部分来进行计数,如何有效地重用?

The differences between the two HQL queries are: 两个HQL查询之间的区别是:

  1. the selection is count(?) , instead of the pojo or property (or list of) 选择是count(?) ,而不是pojo或属性(或列表)
  2. the fetches should not happen, so some tables should not be joined 提取不应发生,因此某些表不应联接
  3. the order by should disappear order by应消失

Is there other differences? 还有其他区别吗?

Do you have coding best-practices to achieve this reuse efficiently (concerns: effort, clarity, performance)? 您是否有编码最佳实践来有效地实现这种重用 (问题:努力,清晰,性能)?

Example for a simple HQL query: 一个简单的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

UPDATED 更新

I received answers on: 我收到以下答案:

  • using Criteria (but we use HQL mostly) 使用条件 (但我们主要使用HQL)
  • manipulating the String query (but everybody agrees it seems complicated and not very safe) 处理String查询(但每个人都同意这似乎很复杂,也不是很安全)
  • wrapping the query , relying on database optimization (but there is a feeling that this is not safe) 包装查询 ,依靠数据库优化(但是有一种感觉,这是不安全的)

I was hoping someone would give options along another path, more related to String concatenation. 我希望有人会沿着另一条路径提供更多选项,这些路径与String串联更多相关。
Could we build both HQL queries using common parts ? 我们可以使用公共部分来构建两个HQL查询吗?

Have you tried making your intentions clear to Hibernate by setting a projection on your (SQL?)Criteria? 您是否尝试过通过在(SQL?)标准上设置投影来使自己的意图清晰地进入休眠状态? I've mostly been using Criteria, so I'm not sure how applicable this is to your case, but I've been using 我主要使用的是Criteria,所以我不确定这对您的情况是否适用,但是我一直在使用

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

and letting Hibernate figure out the caching / reusing / smart stuff by itself.. Not really sure how much smart stuff it actually does though.. Anyone care to comment on this? 并让Hibernate自己找出缓存/重用/智能内容。.不太确定它实际上有多少智能内容。.有人在乎对此发表评论吗?

Well, I'm not sure this is a best-practice, but is my-practice :) 好吧,我不确定这是最佳实践,但我的实践是:)

If I have as query something like: 如果我有类似查询:

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

And I just want to know how many results will get, I execute: 我只想知道会得到多少结果,我执行:

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

And then get the result as an Integer, without mapping results in a POJO. 然后以整数形式获取结果,而不在POJO中映射结果。

Parse your query for remove some parts, like 'order by' is very complicated. 解析查询以删除某些部分,例如“ order by”非常复杂。 A good RDBMS will optimize your query for you. 一个好的RDBMS将为您优化查询。

Good question. 好问题。

Nice question. 好问题。 Here's what I've done in the past (many things you've mentioned already): 这是我过去所做的(您已经提到的许多事情):

  1. Check whether SELECT clause is present. 检查是否存在SELECT子句。
    1. If it's not, add select count(*) 如果不是,则添加select count(*)
    2. Otherwise check whether it has DISTINCT or aggregate functions in it. 否则,请检查它是否具有DISTINCT或聚合函数。 If you're using ANTLR to parse your query, it's possible to work around those but it's quite involved. 如果您正在使用ANTLR来解析查询,则可以解决这些问题,但是涉及很多。 You're likely better off just wrapping the whole thing with select count(*) from () . 您最好只用select count(*) from ()包装整个东西。
  2. Remove fetch all properties 删除fetch all properties
  3. Remove fetch from joins if you're parsing HQL as string. 如果要将HQL解析为字符串,请从联接中删除fetch存。 If you're truly parsing the query with ANTLR you can remove left join entirely; 如果您使用ANTLR真正解析查询,则可以完全删除left join it's rather messy to check all possible references. 检查所有可能的引用非常麻烦。
  4. Remove order by 删除order by
  5. Depending on what you've done in 1.2 you'll need to remove / adjust group by / having . 根据您在1.2中所做的操作,您将需要删除/调整group by / having

The above applies to HQL, naturally. 以上自然适用于HQL。 For Criteria queries you're quite limited with what you can do because it doesn't lend itself to manipulation easily. 对于Criteria查询,您只能做些限制,因为它不便于操作。 If you're using some sort of a wrapper layer on top of Criteria, you will end up with equivalent of (limited) subset of ANTLR parsing results and could apply most of the above in that case. 如果您在Criteria之上使用某种包装层,则最终将得到ANTLR解析结果的(有限)子集,并且在这种情况下可以应用上面的大多数内容。

Since you'd normally hold on to offset of your current page and the total count, I usually run the actual query with given limit / offset first and only run the count(*) query if number of results returns is more or equal to limit AND offset is zero (in all other cases I've either run the count(*) before or I've got all the results back anyway). 由于您通常会保留当前页面的偏移量和总计数,因此我通常会先以给定的限制/偏移量运行实际查询,并且仅在返回的结果数大于或等于限制时才运行count(*)查询AND偏移量为零(在所有其他情况下,我要么先运行count(*) ,要么反过来获得所有结果)。 This is an optimistic approach with regards to concurrent modifications, of course. 当然,对于并发修改,这是一种乐观的方法。

Update (on hand-assembling HQL) 更新 (手工组装HQL)

I don't particularly like that approach. 我不太喜欢这种方法。 When mapped as named query, HQL has the advantage of build-time error checking (well, run-time technically, because SessionFactory has to be built although that's usually done during integration testing anyway). 将HQL映射为命名查询时,它具有构建时错误检查的优势(从技术上来说,运行时是因为必须构建SessionFactory,尽管无论如何通常都是在集成测试中完成)。 When generated at runtime it fails at runtime :-) Doing performance optimizations isn't exactly easy either. 当在运行时生成时,它会在运行时失败:-)进行性能优化也不是一件容易的事。

Same reasoning applies to Criteria, of course, but it's a bit harder to screw up due to well-defined API as opposed to string concatenation. 当然,同样的推理也适用于Criteria,但是由于定义良好的API(而不是字符串连接),因此更难解决。 Building two HQL queries in parallel (paged one and "global count" one) also leads to code duplication (and potentially more bugs) or forces you to write some kind of wrapper layer on top to do it for you. 并行构建两个HQL查询(分页为一个,“全局计数”为一个)也会导致代码重复(并可能有更多的错误),或者迫使您在顶部编写某种包装层来为您完成。 Both ways are far from ideal. 两种方法都不理想。 And if you need to do this from client code (as in over API), the problem gets even worse. 而且,如果您需要从客户端代码执行此操作(如通过API进行操作),问题将变得更加严重。

I've actually pondered quite a bit on this issue. 实际上,我在这个问题上已经考虑了很多 Search API from Hibernate-Generic-DAO seems like a reasonable compromise; Hibernate-Generic-DAO的 Search API似乎是一个合理的折衷方案。 there are more details in my answer to the above linked question. 我对上述链接问题的回答中有更多详细信息。

In a freehand HQL situation I would use something like this but this is not reusable as it is quite specific for the given entities 在徒手的HQL情况下,我会使用类似这样的方法,但这是不可重用的,因为它非常特定于给定的实体

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

Do this once and adjust starting number accordingly till you page through. 这样做一次,并相应地调整起始编号,直到翻阅为止。

For criteria though I use a sample like this 对于标准,尽管我使用这样的示例

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;

But this does the count every time by calling ScrollableResults:last() . 但这每次都通过调用ScrollableResults:last()进行计数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM