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
}
这里我们有两个实体:DeptEmployee 和 Department。为了简化说明,假设一个 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 上找到。