# Hibernate HQL（Hiberate 查询语言）示例

> 原文： [https://howtodoinjava.com/hibernate/complete-hibernate-query-language-hql-tutorial/](https://howtodoinjava.com/hibernate/complete-hibernate-query-language-hql-tutorial/)

在本 **HQL 教程**中，了解什么是 Hiberate 查询语言，各种语句的 hql 语法，命名查询和本机 sql 查询，关联和聚合等。

HQL 是一种类似于 SQL 的面向对象的查询语言，但是 HQL 不会对表和列进行操作，而是处理持久化对象及其属性。 这是 **hql 与 sql** 之间的主要区别。 HQL 是 JPQL（Java 持久化查询语言）的超集。 JPQL 查询是有效的 HQL 查询，但并非所有 HQL 查询都是有效的 JPQL 查询。

HQL 是一种具有自己的语法和语法的语言。 它以字符串形式编写，例如`from Product p`。 Hibernate 将 HQL 查询转换为常规 SQL 查询。 [Hibernate](//howtodoinjava.com/hibernate-tutorials/ "Hibernate Tutorials") 还提供了一个 API，它使我们也可以直接发出 SQL 查询。

请注意，Hibernator 的查询功能不允许您更改数据库结构。 我们只能更改表中的数据。

```java
Table Of Contents

1\. HQL Syntax
	1.1\. Update Operation
	1.2\. Delete Operation
	1.3\. Insert Operation
	1.4\. Select Operation
2\. The from Clause and Aliases
3\. The select Clause and Projection
4\. Using Named Parameters
5\. Paging Through the Result Set
6\. Obtaining a Unique Result
7\. Sorting Results with the order by Clause
8\. Associations
9\. Aggregate Methods
10\. Named Queries
11\. Using Native SQL
12\. Enable Logging and Commenting
```

让我们更详细地讨论每一项，从基本内容到更复杂的概念。

## 1\. HQL 语法

HQL 语法定义为 [**ANTLR**](https://en.wikipedia.org/wiki/ANTLR "ANTLR") 语法。 语法文件包含在 Hibernate 核心下载的语法目录中。 （*ANTLR 是用于构建语言解析器*的工具）。 让我们在这里概述四个基本 CRUD 操作的语法：

#### 1.1 HQL 更新语句

`UPDATE`更改数据库中现有对象的详细信息。 不管是否受管，内存中实体都不会更新以反映由于发出`UPDATE`语句而导致的更改。 这是`UPDATE`语句的语法：

```java
UPDATE [VERSIONED]
   [FROM] path [[AS] alias] [, ...]
   SET property = value [, ...]
   [WHERE logicalExpression]

```

*   `path` – 一个或多个实体的全限定名称
*   `alias` – 用于缩写对特定实体或其属性的引用，并且在查询中的属性名称不明确时必须使用。
*   `VERSIONED` – 表示更新将更新时间戳（如果有），该时间戳是要更新的实体的一部分。
*   `property` – 在`FROM`路径中列出的实体的属性名称。
*   `logicalExpression` – `where`子句。

正在进行的更新示例如下所示。 在此示例中，我们使用 **hql 多列更新查询**来更新员工数据。

```java
Query query=session.createQuery("update Employee set age=:age where name=:name");
query.setInteger("age", 32);
query.setString("name", "Lokesh Gupta");
int modifications=query.executeUpdate();

```

#### 1.2 HQL 删除语句

`DELETE`从数据库中删除现有对象的详细信息。 内存中实体将不会更新以反映`DELETE`语句导致的更改。 这也意味着使用 HQL 进行的删除将不遵循 Hibernate 的级联规则。 但是，如果您在数据库级别指定了级联删除（直接或通过 Hibernate，使用`@OnDelete`注解），则数据库仍将删除子行。

这是`DELETE`语句的语法：

```java
DELETE
   [FROM] path [[AS] alias]
   [WHERE logicalExpression]

```

实际上，删除操作可能如下所示：

```java
Query query=session.createQuery("delete from Account where accountstatus=:status");
query.setString("status", "purged");
int rowsDeleted=query.executeUpdate();

```

#### 1.3 HQL 插入语句

HQL `INSERT`**不能用于直接插入任意实体** - 它只能用于插入从`SELECT`查询获得的信息中构造的实体（与普通 SQL 不同，在 SQL 中，可以使用`INSERT`命令插入任意数据，插入表格，以及插入从其他表格中选择的值）。

这是`INSERT`语句的语法：

```java
INSERT
   INTO path ( property [, ...])
   select

```

实体的名称是`path`。 属性名称是合并的`SELECT`查询的`FROM`路径中列出的实体的属性名称。 该选择查询是 HQL `SELECT`查询（如下一节所述）。

由于此 HQL 语句只能使用 HQL 选择提供的数据，因此其应用可能受到限制。 在实际清除用户之前将用户复制到清除表的示例可能如下所示：

```java
Query query=session.createQuery("insert into purged_accounts(id, code, status) "+
    "select id, code, status from account where status=:status");
query.setString("status", "purged");
int rowsCopied=query.executeUpdate();

```

#### 1.4 HQL 选择语句

HQL `SELECT`用于查询数据库中的类及其属性。 这是`SELECT`语句的语法：

```java
[SELECT [DISTINCT] property [, ...]]
   FROM path [[AS] alias] [, ...] [FETCH ALL PROPERTIES]
   WHERE logicalExpression
   GROUP BY property [, ...]
   HAVING logicalExpression
   ORDER BY property [ASC | DESC] [, ...]

```

实体的标准名称为`path`。 `alias`名称可以用于缩写对特定实体或其属性的引用，并且在查询中使用的属性名称不明确时，必须使用`alias`名称。

`property`名称是路径中中列出的实体的属性的名称。

如果使用`*`**获取所有属性**，则将忽略延迟加载语义，并且将主动加载所有检索到的对象的即时属性（这不适用于递归） 。

`WHERE`用于使用`where`子句创建 **hql 选择查询**。

如果列出的属性仅由`FROM`子句中的别名组成，则可以在 HQL 中省略`SELECT`子句。 如果我们将 JPA 与 JPQL 一起使用，则 HQL 和 JPQL 之间的区别之一是 JPQL 中需要`SELECT`子句。

## 2\. HQL – `from`子句和别名

HQL 中要注意的最重要功能是**别名**。 Hibernate 允许我们使用`as`子句为查询中的类分配别名。 使用别名引用回查询中的类。

举个例子：

```java
from Product as p

//or

from Product as product

```

`as`关键字是可选的。 我们还可以在类名之后直接指定别名，如下所示：

```java
from Product product

```

如果我们需要完全限定 HQL 中的类名，只需指定包和类名即可。 Hibernate 将在后台处理大部分此类操作，因此仅当我们的应用中具有重复名称的类时，我们才真正需要这样做。 如果我们必须在 Hibernate 中执行此操作，请使用如下语法：

```java
from com.howtodoinjava.geo.usa.Product

```

`from`子句非常基础，对于直接使用对象很有用。 但是，如果要使用对象的属性而不将完整的对象加载到内存中，则必须使用`select`子句，如下一节所述。

## 3\. HQL `select`子句和投影

与`from`子句相比，`select`子句对结果集的控制更多。 如果要获取结果集中对象的属性，请使用`select`子句。 例如，我们可以对仅返回名称的数据库产品进行投影查询，而不是将整个对象加载到内存中，如下所示：

```java
select product.name from Product product

```

该查询的结果集将包含一个 Java `String`对象列表。 此外，我们可以检索数据库中每种产品的价格和名称，如下所示：

```java
select product.name, product.price from Product product

```

如果您只对一些属性感兴趣，则可以使用这种方法减少到数据库服务器的网络流量，并在应用的计算机上节省内存。

## 4\. HQL 命名参数

Hibernate 在其 HQL 查询中支持命名参数。 这使得编写查询以接受来自用户的输入的查询变得容易，并且您不必防御 SQL 注入攻击。

使用 JDBC 查询参数时，无论何时添加，更改或删除 SQL 语句的各个部分，都需要更新设置其参数的 Java 代码，因为参数是根据它们在语句中出现的顺序进行索引的。 Hibernate 允许您为 HQL 查询中的参数提供名称，因此您不必担心在查询中意外移动参数。

命名参数的最简单示例对参数使用常规 SQL 类型：

```java
String hql = "from Product where price > :price";
Query query = session.createQuery(hql);
query.setDouble("price",25.0);
List results = query.list();

```

## 5\. HQL – 对结果集分页

通过数据库查询的结果集进行分页是一种非常常见的应用模式。 通常，将分页用于返回大量查询数据的 Web 应用。 Web 应用将浏览数据库查询结果集以为用户构建适当的页面。 如果 Web 应用将每个用户的所有数据都加载到内存中，则该应用将非常慢。 相反，您可以浏览结果集并检索要一次显示一个块的结果。

查询界面上有两种分页方法：`setFirstResult()`和`setMaxResults()`。 `setFirstResult()`方法采用一个代表结果集中第一行的整数，从第 0 行开始。您可以使用`setMaxResults()`方法告诉 Hibernate 仅检索固定数量的对象。 您的 HQL 保持不变 - 您只需要修改执行查询的 Java 代码即可。

```java
Query query = session.createQuery("from Product");
query.setFirstResult(1);
query.setMaxResults(2);
List results = query.list();
displayProductsList(results);

```

如果打开 SQL 日志记录，则可以查看 Hibernate 用于分页的 SQL 命令。 对于开源 HSQLDB 数据库，Hibernate 使用`top`和`limit`。 Microsoft SQL Server 不支持`limit`命令，因此 Hibernate 仅使用`top`命令。 如果您的应用在分页时遇到性能问题，这对于调试非常有用。

如果 HQL 结果集中只有一个结果，则 Hibernate 提供了一种仅获取该对象的快捷方法，如下所述。

## 6\. HQL – 获得独特的结果

HQL 的查询界面提供了一种`uniqueResult()`方法，用于仅从 HQL 查询中获取一个对象。 尽管查询可能只产生一个对象，但如果将结果限制为仅第一个结果，则也可以将`uniqueResult()`方法与其他结果集一起使用。 您可以使用上一节中讨论的`setMaxResults()`方法。

`Query`对象上的`uniqueResult()`方法返回单个对象；如果结果为零，则返回`null`。 如果结果不止一个，则`uniqueResult()`方法将引发`NonUniqueResultException`。

```java
String hql = "from Product where price>25.0";
Query query = session.createQuery(hql);
query.setMaxResults(1);
Product product = (Product) query.uniqueResult();

```

## 7\. HQL – 使用`order by`子句对结果进行排序

要对 HQL 查询的结果进行排序，您需要使用`order by`子句。 您可以按结果集中对象的任何属性对结果进行排序：升序（`asc`）或降序（`desc`）。 如果需要，可以对查询中的多个属性使用排序。 用于排序结果的典型 HQL 查询如下所示：

```java
from Product p where p.price>25.0 order by p.price desc

```

如果要按多个属性进行排序，则只需将其他属性添加到`order by`子句的末尾，并以逗号分隔。 例如，您可以按产品价格和供应商名称进行排序，如下所示：

```java
from Product p order by p.supplier.name asc, p.price asc
```


## 8\. HQL 关联

关联使您可以在 HQL 查询中使用一个以上的类，就像 SQL 允许您在关系数据库中的表之间使用连接一样。 您可以使用`join`子句向 HQL 查询添加关联。 Hibernate 支持五种不同类型的连接：内部连接，交叉连接，左外部连接，右外部连接和完全外部连接。

如果使用交叉连接，只需在`from`子句中指定两个类（`from Product p, Supplier`）。 对于其他连接，请在`from`子句后使用`join`子句。 指定连接的类型，要连接的对象属性以及其他类的别名。

您可以使用内部连接获取每个产品的供应商，然后检索供应商名称，产品名称和产品价格，如下所示：

```java
select s.name, p.name, p.price from Product p inner join p.supplier as s
```

您可以使用类似的语法检索对象：

```java
from Product p inner join p.supplier as s

```

## 9，HQL 聚合方法

HQL 支持多种聚合方法，类似于 SQL。 它们在 HQL 中的工作方式与在 SQL 中的工作方式相同，因此您不必学习任何特定的 Hibernate 术语。 区别在于，在 HQL 中，聚合方法适用于持久对象的属性。 您可以使用`count(*)`语法对结果集中的所有对象进行计数，或使用`count(product.name)`对具有`name`属性的结果集中的对象数进行计数。 以下是使用`count(*)`方法对所有产品进行计数的示例：

```java
select count(*) from Product product

```

通过 HQL 可用的聚合功能包括：

1.  `avg(property name)`：属性值的平均值
2.  `count(property name or *)`：结果中属性出现的次数
3.  `max(property name)`：属性值的最大值
4.  `min(property name)`：属性值的最小值
5.  `sum(property name)`：属性值的总和

## 10\. HQL 命名查询

命名查询是通过实体上的类级注解创建的； 通常，查询适用于在其源文件中出现的实体，但是并没有绝对的要求。

使用`@NamedQueries`注解创建命名查询，该注解包含`@NamedQuery`集的数组； 每个都有一个查询和一个名称。

命名查询的示例可能如下所示：

```java
@NamedQueries({
        @NamedQuery(name = "supplier.findAll", query = "from Supplier s"),
        @NamedQuery(name = "supplier.findByName",
                query = "from Supplier s where s.name=:name"),
})

```

执行上述命名查询更为简单。

```java
Query query = session.getNamedQuery("supplier.findAll");
List<Supplier> suppliers = query.list();

```

> 阅读更多 – [Hiberate 命名查询教程](//howtodoinjava.com/hibernate/hibernate-named-query-tutorial/ "named queries")

## 11\. HQL – 原生 SQL

尽管您应该尽可能使用 HQL，但是 Hibernate 确实提供了一种直接通过 Hibernate 使用本机 SQL 语句的方法。 使用本机 SQL 的原因之一是您的数据库通过其 SQL 方言支持 HQL 中不支持的某些特殊功能。 另一个原因是您可能想从 Hibernate 应用中调用存储过程。

您可以修改 SQL 语句，使其与 Hibernate 的 ORM 层一起使用。 您确实需要修改 SQL 以包括与对象或对象属性相对应的 Hibernate 别名。 您可以使用`{objectname.*}`指定对象的所有属性，也可以直接使用`{objectname.property}`指定别名。

Hibernate 使用这些映射将您的对象属性名称转换为它们的基础 SQL 列。 这可能不是您期望 Hibernate 正常工作的确切方式，因此请注意，您确实需要修改 SQL 语句以获得完整的 ORM 支持。 您尤其会在带有子类的类上遇到本机 SQL 的问题 - 确保您了解如何在单个表或多个表之间映射继承，以便从表中选择正确的属性。

Hibernate 原生 SQL 支持的基础是`org.hibernate.SQLQuery`接口，该接口扩展了`org.hibernate.Query`接口。 您的应用将使用`Session`接口上的`createSQLQuery()`方法从会话中创建本机 SQL 查询。

```java
public SQLQuery createSQLQuery(String queryString) throws HibernateException

```

将包含 SQL 查询的字符串传递给`createSQLQuery()`方法后，应将 SQL 结果与现有的 Hibernate 实体，连接或标量结果相关联。 `SQLQuery`接口具有`addEntity()`，`addJoin()`和`addScalar()`方法。

#### 11.1 Hibernate SQL 查询示例

将本机 SQL 与标量结果一起使用是使用本机 SQL 入门的最简单方法。 示例 Java 代码如下所示：

```java
String sql = "select avg(product.price) as avgPrice from Product product";
SQLQuery query = session.createSQLQuery(sql);
query.addScalar("avgPrice",Hibernate.DOUBLE);
List results = query.list();

```

返回上一对象结果集的**本机 SQL** 比上一个示例复杂一点。 在这种情况下，我们将需要将实体映射到 SQL 查询。

```java
String sql = "select {supplier.*} from Supplier supplier";
SQLQuery query = session.createSQLQuery(sql);
query.addEntity("supplier", Supplier.class);
List results = query.list();

//Hibernate modifies the SQL and executes the following command against the database:

select Supplier.id as id0_, Supplier.name as name2_0_ from Supplier supplier

```

## 12\. HQL – 启用日志和注解

Hibernate 可以将 HQL 查询背后的基础 SQL 输出到应用的日志文件中。 如果 HQL 查询未提供您期望的结果，或者查询花费的时间比您想要的长，则此功能特别有用。 这不是您必须经常使用的功能，但是在您必须向数据库管理员寻求帮助来调整 Hibernate 应用时，此功能很有用。

#### 12.1 HQL 日志

查看 Hibernate HQL 查询的 SQL 的最简单方法是使用“`show_sql`”属性在日志中启用 SQL 输出。 在`hibernate.cfg.xml`配置文件中将此属性设置为`true`，Hibernate 会将 SQL 输出到日志中。 当您在应用的输出中查找 Hibernate SQL 语句时，它们的前缀将为“`Hibernate:`”。

如果您将 log4j 日志记录为 Hibernate 类进行调试，则您将在日志文件中看到 SQL 语句，以及有关 Hibernate 如何解析 HQL 查询并将其转换为 SQL 的大量信息。

#### 12.2 HQL 注解

跟踪 HQL 语句到生成的 SQL 可能很困难，因此 Hibernate 在`Query`对象上提供了注解功能，使您可以将注解应用于特定查询。 `Query`接口具有`setComment()`方法，该方法将`String`对象作为参数，如下所示：

```java
public Query setComment(String comment)

```

即使没有使用`setComment()`方法，Hibernate 也不会在没有其他配置的情况下将注解添加到 SQL 语句中。 您还需要在 Hibernate 配置中将 Hibernate 属性`hibernate.use_sql_comments`设置为`true`。

如果您设置了此属性，但没有以编程方式在查询中设置注解，则 Hibernate 将在注解中包括用于生成 SQL 调用的 HQL。 我发现这对于调试 HQL 非常有用。

如果启用了 SQL 日志记录，请使用注解来标识应用日志中的 SQL 输出。

目前，这一切都与 **HQL 教程**有关。 继续访问以了解更多有关 Hiberate 的信息。

学习愉快！