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 可以有多个 DeptEmployee

3. 自定义查询结果类

假设我们只想列出所有员工的名字及其所属部门的名称。

通常我们会这样写查询:

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

这会把所有员工、所有属性、以及关联的部门都查出来。

✅ 但如果只是需要员工名和部门名的话,这样做就有点浪费资源了。

一种优化方式是只选择我们需要的字段:

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

❌ 返回的是 Object[] 数组,处理起来不太方便。

我们可以让 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.baeldung.hibernate.pojo.Result(m.name, m.department.name)" 
  + " from com.baeldung.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 对象。

✅ 相比于返回 Object[],这种做法更清晰易读。

⚠️ 要注意的是,在 HQL 中必须使用类的全限定名(fully qualified name)。

5. 使用 ResultTransformer

除了使用构造函数,还可以通过 ResultTransformer 来实现同样的效果:

Query query = session.createQuery("select m.name as employeeName, m.department.name as departmentName" 
  + " from com.baeldung.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 类中的属性名一致。

⚠️ 注意:从 Hibernate 5.2 开始,Query.setResultTransformer() 已被标记为废弃(deprecated)。

6. 将 Query.list() 强制转换为 List

Query.list() 的结果强制转换为特定类型是一个常见操作,关键是确保转换的目标类型与查询返回的对象类型一致。

6.1. 获取并转换 List

获取并转换查询结果的代码如下:

List<Result> results = query.list();

这里假定查询返回的是 Result 类型的对象列表。如果你使用的是其他类型,请相应替换。

另外,根据不同版本的 Hibernate,也可能需要使用 getResultList() 替代 list()

6.2. 处理类型安全和警告

虽然上面的转换方式很常见,但它可能会产生“unchecked cast”警告。

✅ 为了避免这类警告,可以加上注解:

@SuppressWarnings("unchecked")
List<Result> results = query.list();

⚠️ 确保你的转换类型与实际查询返回的类型一致,避免运行时错误。

7. 总结

本文介绍了如何使用自定义类来接收 Hibernate 查询结果,从而提升代码的可读性和性能。

配套源码可以在 GitHub 上找到。


原始标题:Mapping A Hibernate Query To A Custom Class | Baeldung