为什么 hibernate hql 不同会导致左连接上的 sql 不同?
我已经得到了这个测试 HQL:
select distinct o from Order o left join fetch o.lineItems
它确实生成一个 SQL 不同的结果,但没有明显的原因:
select distinct order0_.id as id61_0_, orderline1_.order_id as order1_62_1_...
SQL 结果集始终相同(有和没有 SQL 不同):
order id | order name | orderline id | orderline name
---------+------------+--------------+---------------
1 | foo | 1 | foo item
1 | foo | 2 | bar item
1 | foo | 3 | test item
2 | empty | NULL | NULL
3 | bar | 4 | qwerty item
3 | bar | 5 | asdfgh item
为什么 hibernate 会生成 SQL 不同? SQL 不同没有任何意义,并且使查询速度比需要的慢。 这与提到HQL在这种情况下是不同的HQL只是结果变压器的捷径:
session.createQuery("选择不同的o from Order o left join 获取 o.lineItems").list();
看起来您在这里使用了 SQL DISTINCT 关键字。当然,这不是SQL,这是HQL。在本例中,这个不同只是结果转换器的快捷方式。是的,在其他情况下,HQL DISTINCT 将直接转换为 SQL DISTINCT。不是在这种情况下:您无法在 SQL 级别过滤掉重复项,产品/联接的本质禁止这样做 - 您需要重复项,否则您无法获得所需的所有数据。
谢谢。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
仔细看看 hibernate 生成的 sql 语句 - 是的,它确实使用了“distinct”关键字,但不是以我认为您期望的方式(或 Hibernate FAQ 暗示的方式)即返回一组“不同的”或“独特的”订单。
它不使用 unique 关键字返回不同的订单,因为考虑到您还指定了联接,这在该 SQL 查询中没有意义。
生成的 sql 集仍然需要由 ResultTransformer 处理,因为显然 sql 集包含重复的订单。这就是为什么他们说 HQL 不同关键字不直接映射到 SQL 不同关键字。
Have a closer look at the sql statement that hibernate generates - yes it does use the "distinct" keyword but not in the way I think you are expecting it to (or the way that the Hibernate FAQ is implying) i.e. to return a set of "distinct" or "unique" orders.
It doesn't use the distinct keyword to return distinct orders, as that wouldn't make sense in that SQL query, considering the join that you have also specified.
The resulting sql set still needs processing by the ResultTransformer, as clearly the sql set contains duplicate orders. That's why they say that the HQL distinct keyword doesn't directly map to the SQL distinct keyword.
我遇到了完全相同的问题,我认为这是一个 Hibernate 问题(不是错误,因为代码不会失败)。然而,我必须更深入地挖掘以确保这是一个问题。
Hibernate(至少在版本 4 中,这是我正在处理我的项目的版本,特别是 4.3.11)使用 SPI 的概念,长话短说:它就像一个 API 来扩展或修改框架。
我利用此功能来替换类
org.hibernate.hql.internal.ast.ASTQueryTranslatorFactory
(该类由 Hibernate 调用并委托生成 SQL 查询的工作)和org .hibernate.hql.internal.ast.QueryTranslatorImpl
(这是一种内部类,由 org.hibernate.hql.internal.ast.ASTQueryTranslatorFactory 调用并生成实际的 SQL 查询)。我做了如下:替换
org.hibernate.hql.internal.ast.ASTQueryTranslatorFactory
:替换
org.hibernate.hql.internal.ast.QueryTranslatorImpl
:如果您按照代码流程,您会注意到我刚刚修改了方法
private voidgenerate(AST sqlAst) throws QueryException, RecognitionException
并添加了以下几行:我对此代码所做的是删除 unique 关键字从生成的 SQL 查询。
创建上面的类后,我在 hibernate 配置文件中添加了以下行:
该行告诉 hibernate 使用我的自定义类来解析 HQL 查询并生成不带 unique 关键字的 SQL 查询。请注意,我在原始 HQL 解析器所在的同一包中创建了自定义类。
I had the exact same problem and I think this is an Hibernate issue (not a bug because code doesn't fail). However, I have to dig deeper to make sure it's an issue.
Hibernate (at least in version 4 which its the version I'm working on my project, specifically 4.3.11) uses the concept of SPI, long story short: its like an API to extend or modify the framework.
I took advantage of this feature to replace the classes
org.hibernate.hql.internal.ast.ASTQueryTranslatorFactory
(This class is called by Hibernate and delegates the job of generating the SQL query) andorg.hibernate.hql.internal.ast.QueryTranslatorImpl
(This is sort of an internal class which is called byorg.hibernate.hql.internal.ast.ASTQueryTranslatorFactory
and generates the actual SQL query). I did it as follows:Replacement for
org.hibernate.hql.internal.ast.ASTQueryTranslatorFactory
:Replacement for
org.hibernate.hql.internal.ast.QueryTranslatorImpl
:If you follow the code flow you will notice that I just modified the method
private void generate(AST sqlAst) throws QueryException, RecognitionException
and added the a following lines:What I do with this code is to remove the distinct keyword from the generated SQL query.
After creating the classes above, I added the following line in the hibernate configuration file:
This line tells hibernate to use my custom class to parse HQL queries and generate SQL queries without the distinct keyword. Notice I created my custom classes in the same package where the original HQL parser resides.