Contents

Hibernate 命名查询

1. 概述

将 HQL 和 SQL 分散在数据访问对象中的一个主要缺点是它使代码不可读。因此,将所有 HQL 和 SQL 分组到一个位置并在实际数据访问代码中仅使用它们的引用可能是有意义的。幸运的是,Hibernate 允许我们使用命名查询来做到这一点。

命名查询是具有预定义的不可更改查询字符串的静态定义查询。它们在创建会话工厂时进行验证,从而使应用程序在出现错误时快速失败。

在本文中,我们将了解如何使用@NamedQuery@NamedNativeQuery*注解定义和使用 Hibernate 命名查询。*

2. 实体

让我们先看看我们将在本文中使用的实体:

@Entity
public class DeptEmployee {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private long id;
    private String employeeNumber;
    private String designation;
    private String name;
    @ManyToOne
    private Department department;
    // getters and setters
}

在我们的示例中,我们将根据员工编号检索员工。

3. 命名查询

要将其定义为命名查询,我们将使用org.hibernate.annotations.NamedQuery注解。它使用 Hibernate 特性扩展了 javax.persistence.NamedQuery

我们将其定义为DeptEmployee类的注解:

@org.hibernate.annotations.NamedQuery(name = "DeptEmployee_findByEmployeeNumber", 
  query = "from DeptEmployee where employeeNumber = :employeeNo")

需要注意的是,每个 @NamedQuery注解都只附加到一个实体类或映射的超类。但是, 由于命名查询的范围是整个持久单元,我们应该仔细选择查询名称以避免冲突。 我们通过使用实体名称作为前缀来实现这一点。

如果我们对一个实体有多个命名查询,我们将使用 @NamedQueries 注解对它们进行分组:

@org.hibernate.annotations.NamedQueries({
    @org.hibernate.annotations.NamedQuery(name = "DeptEmployee_FindByEmployeeNumber", 
      query = "from DeptEmployee where employeeNumber = :employeeNo"),
    @org.hibernate.annotations.NamedQuery(name = "DeptEmployee_FindAllByDesgination", 
      query = "from DeptEmployee where designation = :designation"),
    @org.hibernate.annotations.NamedQuery(name = "DeptEmployee_UpdateEmployeeDepartment", 
      query = "Update DeptEmployee set department = :newDepartment where employeeNumber = :employeeNo"),
...
})

请注意,HQL 查询可以是 DML 样式的操作。因此,它不需要只是一个 select语句。例如,我们可以在上面的 DeptEmployee_UpdateEmployeeDesignation中进行更新查询。

3.1. 配置查询功能

我们可以使用*@NamedQuery*注解设置各种查询功能。让我们看一个例子:

@org.hibernate.annotations.NamedQuery(
  name = "DeptEmployee_FindAllByDepartment", 
  query = "from DeptEmployee where department = :department",
  timeout = 1,
  fetchSize = 10
)

在这里,我们已经配置了超时间隔和获取大小。除了这两个,我们还可以设置功能,例如:

  • cacheable – 查询(结果)是否可缓存
  • cacheMode – 用于此查询的缓存模式;这可以是GET、IGNORE、NORMAL、PUT或 REFRESH之一
  • cacheRegion – 如果查询结果是可缓存的,则命名要使用的查询缓存区域
  • *comment * – 添加到生成的 SQL 查询的注释;针对 DBA
  • flushMode – 此查询的刷新模式,ALWAYS、AUTO、COMMIT、MANUAL或 PERSISTENCE_CONTEXT之一

3.2. 使用命名查询

现在我们已经定义了命名查询,让我们用它来检索员工:

Query<DeptEmployee> query = session.createNamedQuery("DeptEmployee_FindByEmployeeNumber", 
  DeptEmployee.class);
query.setParameter("employeeNo", "001");
DeptEmployee result = query.getSingleResult();

在这里,我们使用了createNamedQuery 方法。它接受查询的名称并返回一个org.hibernate.query.Query对象。

4. 命名原生查询

除了 HQL 查询,我们还可以将原生 SQL 定义为命名查询。为此,我们可以使用*@NamedNativeQuery*注解。虽然它类似于 @NamedQuery,但它需要更多的配置。

让我们用一个例子来探索这个注解:

@org.hibernate.annotations.NamedNativeQueries(
    @org.hibernate.annotations.NamedNativeQuery(name = "DeptEmployee_GetEmployeeByName", 
      query = "select * from deptemployee emp where name=:name",
      resultClass = DeptEmployee.class)
)

由于这是一个原生查询,我们必须告诉 Hibernate 要将结果映射到哪个实体类。因此,我们使用了 resultClass属性来执行此操作。

映射结果的另一种方法是使用 resultSetMapping属性。在这里,我们可以指定预定义的SQLResultSetMapping的名称。

请注意,我们只能使用 resultClassresultSetMapping之一。

4.1. 使用命名的本机查询

要使用命名原生查询,我们可以使用Session.createNamedQuery()

Query<DeptEmployee> query = session.createNamedQuery("DeptEmployee_FindByEmployeeName", DeptEmployee.class);
query.setParameter("name", "John Wayne");
DeptEmployee result = query.getSingleResult();

或 Session.getNamedNativeQuery()

NativeQuery query = session.getNamedNativeQuery("DeptEmployee_FindByEmployeeName");
query.setParameter("name", "John Wayne");
DeptEmployee result = (DeptEmployee) query.getSingleResult();

这两种方法之间的唯一区别是返回类型。第二种方法返回 NativeQuery,它是 Query的子类。

5. 存储过程和函数

我们也可以使用*@NamedNativeQuery* 注解来定义对存储过程和函数的调用:

@org.hibernate.annotations.NamedNativeQuery(
  name = "DeptEmployee_UpdateEmployeeDesignation", 
  query = "call UPDATE_EMPLOYEE_DESIGNATION(:employeeNumber, :newDesignation)", 
  resultClass = DeptEmployee.class)

请注意,虽然这是一个更新查询,但我们使用了 resultClass属性。这是因为 Hibernate 不支持纯本机标量查询。解决此问题的方法是设置 resultClass或 resultSetMapping