Contents

Hibernate 查询映射到自定义类

1. 概述

当我们使用 Hibernate 从数据库中检索数据时,默认情况下,它使用检索到的数据为请求的对象构建整个对象图。但有时我们可能只想检索部分数据,最好是扁平结构。

在这个快速教程中,我们将看到如何在 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;
    // constructor, getters and setters 
} 
@Entity
public class Department {
 
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private long id;
    private String name;
    @OneToMany(mappedBy="department")
    private List<DeptEmployee> employees;
    public Department(String name) {
        this.name = name;
    }
    
    // getters and setters 
}

在这里,我们有两个实体—— DeptEmployeeDepartment。为简单起见,我们假设一个DeptEmployee只能属于一个Department

但是,一个Department可以有多个DeptEmployees

3. 自定义查询结果类

假设我们要打印所有员工的列表,其中包含他们的姓名和部门名称。

通常,我们会使用这样的查询来检索这些数据:

Query<DeptEmployee> query = session.createQuery("from com.blogdemo.hibernate.entities.DeptEmployee");
List<DeptEmployee> deptEmployees = query.list();

这将检索所有员工、他们的所有属性、关联的部门及其所有属性。

但是,在这种特殊情况下,这可能有点贵,因为我们只需要员工姓名和部门名称。

仅检索我们需要的信息的一种方法是在 select 子句中指定属性。

但是,当我们这样做时,Hibernate 返回一个数组列表而不是对象列表:

Query query = session.createQuery("select m.name, m.department.name from com.blogdemo.hibernate.entities.DeptEmployee m");
List managers = query.list();
Object[] manager = (Object[]) managers.get(0);
assertEquals("John Smith", manager[0]);
assertEquals("Sales", manager[1]);

我们可以看到,返回的数据处理起来有点麻烦。但是,幸运的是,我们可以让 Hibernate 将这些数据填充到一个类中。

让我们看一下Result类,我们将使用它来将检索到的数据填充到:

public class Result {
    private String employeeName;
    
    private String departmentName;
    
    public Result(String employeeName, String departmentName) {
        this.employeeName = employeeName;
        this.departmentName = departmentName;
    }
    public Result() {
    }
    // getters and setters 
}

请注意,该类不是实体,而只是 POJO。但是,我们也可以使用实体,只要它有一个构造函数,该构造函数将我们想要填充的所有属性作为参数。

我们将在下一节中了解为什么构造函数很重要。

4. 在 HQL 中使用构造函数

现在,让我们看看使用这个类的 HQL:

Query<Result> query = session.createQuery("select new com.blogdemo.hibernate.pojo.Result(m.name, m.department.name)" 
  + " from com.blogdemo.hibernate.entities.DeptEmployee m");
List<Result> results = query.list();
Result result = results.get(0);
assertEquals("John Smith", result.getEmployeeName());
assertEquals("Sales", result.getDepartmentName());

在这里,我们使用我们在Result类中定义的构造函数以及我们想要检索的属性。这将返回一个Result对象列表,其中包含从列中填充的数据。

正如我们所见,返回的列表比使用对象数组列表更容易处理。

需要注意的是,我们必须在查询中使用类的完全限定名。

5. 使用 ResultTransformer

在 HQL 查询中使用构造函数的另一种方法是使用ResultTransformer

Query query = session.createQuery("select m.name as employeeName, m.department.name as departmentName" 
  + " from com.blogdemo.hibernate.entities.DeptEmployee m");
query.setResultTransformer(Transformers.aliasToBean(Result.class));
List<Result> results = query.list();
Result result = results.get(0);
assertEquals("John Smith", result.getEmployeeName());
assertEquals("Sales", result.getDepartmentName());

我们使用 *Transformers.aliasToBean()*方法使用检索到的数据来填充 Result对象。

因此,我们必须确保 select 语句中的列名或其别名与 Result类的属性相匹配。

注意Query.setResultTransformer(ResultTransformer) 自 Hibernate 5.2 以来已被弃用。