JPA和Hibernate - Criteria与JPQL或HQL的比较

313
使用CriteriaHQL有哪些优缺点?Criteria API是Hibernate中表达查询的一种不错的面向对象方式,但有时Criteria查询比HQL更难理解/构建。
何时使用Criteria和HQL?在哪些用例中您更喜欢哪个?还是只是口味问题?

答案应该是“取决于用例”。 - Hace
2
这是一个基于观点的问题的定义,但人们还没有利用关闭它的机会...根据网站FAQ。 - user3973283
22个回答

219

我更喜欢使用Criteria查询来进行动态查询。例如,根据某些参数动态添加一些排序规则或者略过某些部分(例如限制条件),这样会更加容易。

另一方面,对于静态和复杂的查询,我使用HQL,因为它更易于理解和阅读。此外,我认为HQL的功能更加强大,例如支持不同类型的联接操作。


14
虽然 Criteria 看起来更加类型安全,但唯一能让你感觉安全的是测试。 - user168237
有没有一些好的例子可以展示在某些情况下为什么 HQL 比 Criteria API 更好?我读了一篇博客的结尾,但是一个字也没看懂。如果你可以帮忙解答,我将不胜感激。谢谢。链接 - http://www.javalobby.org/articles/hibernatequery102/ - Erran Morad
出于以上所有原因,我也更喜欢使用Criteria而不是HQL,因为它对程序员来说更安全,减少了编码错误——在HQL字符串上进行编译没有得到验证。 - nuno
然而,在分页时存在检索不同实体的问题。在这种情况下,我会选择使用HQL来避免出现问题... - ChambreNoire
1
使用元模型的条件查询可以帮助重构时不破坏任何内容,并且通过现代IDE中的简单命令重命名代码中所有出现的列名。 - Massimo

93

在性能方面,使用criteriaQuery查询每次都会为表名创建一个新的别名,这并不反映在任何数据库中上次查询的缓存中。这导致编译生成的SQL的开销增加,需要更长的执行时间。

关于提取策略 [http://www.hibernate.org/315.html]

  • Criteria尊重映射中的惰性设置,并保证加载所需内容。这意味着一个Criteria查询可能会导致多个SQL SELECT语句立即获取所有非惰性映射关联和集合的子图。如果您想更改“如何”甚至“什么”,请使用setFetchMode()为特定集合或关联启用或禁用外部连接提取。 Criteria查询还完全尊重提取策略(联接vs选择vs子选择)。
  • HQL尊重映射中的惰性设置,并保证加载所需内容。这意味着一个HQL查询可能会导致多个SQL SELECT语句立即获取所有非惰性映射关联和集合的子图。如果您想更改“如何”甚至“什么”,请使用LEFT JOIN FETCH为特定集合或可为空的多对一或一对一关联启用外部连接提取,或使用JOIN FETCH为非空的多对一或一对一关联启用内部连接提取。 HQL查询不尊重映射文档中定义的任何fetch="join"。

3
提醒浏览此页面的人,这个回答是在2008年发布的,现在可能已经不再适用。参考链接为:http://dimovelev.blogspot.com/2015/02/performance-pitfalls-hibernate-criteria.html。 - Alkanshel

42

Criteria是面向对象的API,而HQL意味着字符串拼接。这意味着所有面向对象的好处都适用:

  1. 其他条件相同的情况下,面向对象版本更不容易出错。任何旧字符串都可能添加到HQL查询中,而只有有效的Criteria对象才能进入Criteria树。从效果上讲,Criteria类更加受限制。
  2. 通过自动完成,面向对象更易于发现(因此对于我来说更容易使用)。您不一定需要记住查询的哪些部分放在哪里;IDE可以帮助您。
  3. 您也不需要记住语法的细节(比如哪些符号放在哪里)。您只需要知道如何调用方法和创建对象即可。

由于HQL非常类似于SQL(大多数开发人员已经非常了解),所以这些“无需记忆”的论点并没有太大的分量。如果HQL与SQL更不同,则这将变得更加重要。


12
这些论点在涉及HQL时站不住脚。它并不一定涉及字符串拼接。面向对象版本更不易出错的说法未经证实。它同样容易出错,但是错误类型不同。了解调用哪些方法与知道在HQL中调用哪些符号的努力程度并没有太大区别(我是说,认真地讲,我们这里不是在解决偏微分方程)。 - luis.espinal
有没有一些好的例子可以展示在某些情况下为什么HQL比Criteria API更好?我读了一篇博客的结尾,但是一无所获。如果您能帮忙,将不胜感激。谢谢。链接 - http://www.javalobby.org/articles/hibernatequery102/ - Erran Morad
1
HQL的命名查询在部署时被编译,此时检测到缺失的字段(可能是由于不良重构引起的)。我认为这使得代码更具弹性,实际上比criteria更少出现错误。 - narduk
在 Criteria 中的自动完成功能几乎没有用,因为属性只是字符串。 - Lluis Martinez

37

当我不知道输入将用于哪些数据时,我通常会使用Criteria。比如在搜索表单中,用户可以输入任何1到50个项目,而我不知道他们要搜索什么。随着我逐步检查用户正在搜索的内容,只需向条件追加更多条件即可非常容易地实现。在这种情况下,我认为将HQL查询放入其中可能会更加麻烦。不过,如果我确切地知道我想要的查询,那么HQL是非常好的。


1
这是一个好的评论。我们目前为一个包含许多不同对象的搜索表单构建非常大的HQL字符串,通过连接。看起来很丑陋。要看看是否可以使用Criteria来清理它。有趣... - cbmeeks
谢谢。这是一个很好的例子。您能再给我一些吗? - Erran Morad
最好的解释 @Arthur Thomas - SNEHA

33

HQL更容易阅读,使用像Eclipse Hibernate插件这样的工具调试更容易,并且日志记录更容易。 如果需要构建大量行为在运行时决定的动态查询,则Criteria查询更好。 如果您不了解SQL,我可以理解使用Criteria查询,但总的来说,如果我已经预先知道我想要什么,我更喜欢使用HQL。


29

Criteria API

Criteria API更适合于动态生成的查询。因此,如果您想要添加WHERE子句过滤器、JOIN子句或改变ORDER BY子句或投影列,则Criteria API可以帮助您动态生成查询,同时还可以防止SQL注入攻击。

然而,Criteria查询表达能力较弱,甚至可能导致非常复杂和低效的SQL查询。

JPQL和HQL

JPQL是JPA标准实体查询语言,而HQL扩展了JPQL并添加了一些Hibernate特定的功能。

JPQL和HQL具有极高的表达能力,并且类似于SQL。与Criteria API不同,JPQL和HQL使预测由JPA提供程序生成的底层SQL查询变得容易。同时,检查自己的HQL查询比检查Criteria查询容易得多。

值得注意的是,如果需要修改实体,则使用JPQL或Criteria API选择实体是有意义的。否则,DTO投影是更好的选择。

结论

如果不需要改变实体查询结构,则使用JPQL或HQL。如果需要更改过滤或排序条件或更改投影,则使用Criteria API。

然而,仅仅因为使用JPA或Hibernate,并不意味着不使用原生SQL。SQL查询非常有用,而JPQL和Criteria API并不能替代SQL。


你能否帮我看一下我的问题:如何在选择子句中使用构造函数编写多表选定列的HQL JOIN查询。非常感谢! - Shantaram Tupe
我已经长时间关注你的答案/博客了,非常棒。点赞!如果您有时间,能否帮忙解决这个问题 使用GROUP BY和CONCAT的Criteria API和JPQL API? - samshers
关于Native SQL和Criteria,似乎在使用Hibernate的本地函数时存在一些限制。我参考了您的这篇文章->但是它也没有解决“DISTINCT / ORDER BY / SEPERATOR”问题- https://vladmihalcea.com/hibernate-sql-function-jpql-criteria-api-query/。您能否在stackoverflow上的这篇帖子[Criteria API and JPQL API with GROUP BY and GROUP_CONCAT?](https://dev59.com/8MTsa4cB1Zd3GeqPKOKv)中为我们解释一下限制? - samshers
弗拉德 - 很高兴看到你的回复。没问题。谢谢。 - samshers

24

23

Criteria Api是Hibernate中的好概念之一。据我看来,以下是区分HQLCriteria Api的几个要点:

  1. HQL用于在数据上执行选择和非选择操作,而Criteria仅用于选择数据,我们不能使用Criteria执行非选择操作。
  2. HQL适用于执行静态查询,而Criteria适用于执行动态查询。
  3. HQL不支持分页概念,但我们可以使用Criteria实现分页。
  4. 相对于HQL,Criteria的执行时间更长。
  5. 由于其动态查询生成方式,使用Criteria可确保安全免受SQL注入攻击,而在HQL中,由于您的查询要么是固定的,要么是参数化的,所以无法避免SQL注入攻击。

11
要点:HQL 中有分页功能:您可以使用 limit offset:rows在 HQL 中,您可以使用 setParameter 避免 SQL 注入。 - Viswanath Lekshmanan

15
为了更好地兼顾HQL的表达性和简洁性以及Criteria的动态性,可以考虑使用 Querydsl
Querydsl 支持 JPA/Hibernate、JDO、SQL 和集合。
我是 Querydsl 的维护者,所以这个答案有偏见。

14

对我来说,Criteria相当容易理解,并使查询动态化。但我迄今为止发现的缺点是,它加载所有many-one等关系,因为我们只有三种FetchModes,即Select、Proxy和Default,在所有这些情况下,它都会加载many-one(如果我错了,请帮助我:)

Criteria的第二个问题是,它会加载完整的对象,即如果我只想加载员工的EmpName,它不会呈现出来,而是会呈现出完整的Employee对象,我可以从中获取EmpName,由于这个它在报告方面效果很差。而HQL仅仅加载你需要的内容(未加载关联/关系),因此大大提高了性能。

Criteria的一个特点是它会通过动态查询生成来保护您免受SQL注入攻击,而在HQL中,由于您的查询要么是固定的,要么是参数化的,因此不安全。

此外,如果您在aspx.cs文件中编写HQL,则与DAL紧密耦合。

总的来说,我的结论是,在某些地方,您不能没有HQL,比如报告,因此请使用它们,否则Criteria更容易管理。


13
HQL不安全,存在SQL注入风险。 - Varun Mehta
我认为Criteria不是注入安全的。请看我的帖子:https://dev59.com/VVnUa4cB1Zd3GeqPZ2KN - Mister Smith
4
HQL通过添加“setParameter”是可以防止SQL注入攻击的。 - javatar
2
@Zafar:您可以使用投影仅选择实体的特定属性。 - Răzvan Flavius Panda
@Zafar,你可以在条件查询中设置投影以选择特定列。 你可以获取EmpName,无需获取完整对象。 - Khatri

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接