1. 引言
Hibernate 是一款强大的 ORM 框架,能大幅简化 Java 对象与关系型数据库的交互。它将原生 SQL 的复杂性抽象化,让我们能用更面向对象的方式检索实体列表。本文将探讨几种通过 Hibernate 从数据库获取实体列表的实用技术,帮你避开常见坑点。
2. 数据库表到实体的映射
Hibernate 提供了直观的方式将数据库表映射为 Java 实体类。每个实体类对应数据库中的一张表,属性映射到表的列。通过 @Entity
、@Table
、@Id
和 @Column
等注解定义映射关系。
Hibernate 利用这些映射将 JPQL 或 Criteria API 查询转换为高效 SQL。当编写 JPQL 查询如 SELECT e FROM Employee e
时,Hibernate 自动识别 Employee
对应数据库表,e
代表实体实例,最终生成底层 SQL 检索数据。
3. 使用 Criteria API
Criteria API 提供了通过 Java 对象动态构建查询的方案。它允许在运行时构造查询,特别适合查询条件编译时未知或需根据用户输入动态调整的场景。
以下是使用 Criteria API 获取实体列表的示例:
@PersistenceContext
private EntityManager entityManager;
public List<Employee> getAllEmployees() {
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Employee> criteriaQuery = criteriaBuilder.createQuery(Employee.class);
Root<Employee> employeeRoot = criteriaQuery.from(Employee.class);
criteriaQuery.select(employeeRoot);
Query query = entityManager.createQuery(criteriaQuery);
return query.getResultList();
}
此示例获取所有 Employee
实体。CriteriaBuilder
构建查询,**定义根实体 (Employee.class
) 并指定选择所有属性 (criteriaQuery.select(employeeRoot)
)**,最后转换为类型化 Query
对象执行。
Criteria API 支持动态过滤和排序。以下示例按姓氏升序获取 "Engineering" 部门员工:
public List<Employee> getAllEmployeesByDepartment(String department) {
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Employee> criteriaQuery = criteriaBuilder.createQuery(Employee.class);
Root<Employee> employeeRoot = criteriaQuery.from(Employee.class);
Predicate departmentPredicate = criteriaBuilder.equal(employeeRoot.get("department"), department);
Path<Object> sortByPath = employeeRoot.get("lastName");
criteriaQuery.orderBy(criteriaBuilder.asc(sortByPath));
criteriaQuery.select(employeeRoot).where(departmentPredicate);
Query query = entityManager.createQuery(criteriaQuery);
return query.getResultList();
}
通过 criteriaBuilder.equal()
定义部门条件,用 criteriaBuilder.asc()
实现排序。
4. 使用 JPQL (Java Persistence Query Language)
JPQL 提供类似 SQL 的语法,但操作实体和属性而非表和列,提升可读性。
以下是使用 JPQL 获取所有 Employee
实体的示例:
@PersistenceContext
private EntityManager entityManager;
public List<Employee> getAllEmployees() {
String jpqlQuery = "SELECT e FROM Employee e";
Query query = entityManager.createQuery(jpqlQuery, Employee.class);
return query.getResultList();
}
构建 JPQL 字符串 SELECT e FROM Employee e
,通过 EntityManager
的 createQuery()
创建查询对象,设置结果类型为 Employee.class
,最后用 getResultList()
执行。
JPQL 支持 WHERE
过滤和 ORDER BY
排序。以下示例按姓氏升序获取特定部门员工:
public List<Employee> getAllEmployeesByDepartment() {
String jpqlQuery = "SELECT e FROM Employee e WHERE e.department = 'Engineering' ORDER BY e.lastName ASC";
Query query = entityManager.createQuery(jpqlQuery, Employee.class);
return query.getResultList();
}
此查询检索 department
为 "Engineering" 的员工,按 lastName
升序排序。
5. 使用命名查询
虽然 JPQL 适合动态构建查询,但命名查询提供预定义可重用查询的结构化方案,提升代码可读性和可维护性,减少查询字符串错误风险。
Hibernate 提供两种定义命名查询的方式:
5.1. 使用注解
通过 @NamedQuery
注解在实体类中直接定义:
@Entity
@NamedQuery(name = "findAllEmployees", query = "SELECT e FROM Employee e")
@NamedQuery(name = "findEmployeesByDepartment", query = "SELECT e FROM Employee e WHERE e.department = :department ORDER BY e.lastName ASC")
public class Employee {
@Id
@GeneratedValue
private Integer id;
private String lastName;
private String department;
// getter and setter methods
}
定义了两个命名查询:
findAllEmployees
:无过滤条件获取所有员工findEmployeesByDepartment
:按部门参数 (:department
) 过滤,按姓氏升序排序
5.2. 使用 XML 配置
在 hibernate.cfg.xml
中定义:
<hibernate-configuration>
<named-query name="findAllEmployees">
<query>SELECT e FROM Employee e</query>
</named-query>
<named-query name="findEmployeesByDepartment">
<query>SELECT e FROM Employee e WHERE e.department = :department ORDER BY e.lastName ASC</query>
</named-query>
</hibernate-configuration>
XML 配置实现关注点分离和集中管理,注解则更简洁且与实体强关联。
5.3. 使用命名查询
通过 entityManager.createNamedQuery()
执行命名查询:
@PersistenceContext
private EntityManager entityManager;
public List<Employee> getAllEmployees() {
Query query = entityManager.createNamedQuery("findAllEmployees", Employee.class);
return query.getResultList();
}
public List<Employee> findEmployeesByDepartment(String department) {
Query query = entityManager.createNamedQuery("findEmployeesByDepartment", Employee.class);
query.setParameter("department", department);
return query.getResultList();
}
分别演示了获取所有员工和按部门过滤员工的使用方式。
6. 一对多关系
Hibernate 中可通过 getter 方法直接访问关联实体集合。一对多关系允许单个父实体关联多个子实体,在获取父实体列表时,常需同时加载关联子实体。
以下示例展示获取 Department
实体及其关联 Employee
实体:
@Entity
public class Department {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToMany(mappedBy = "department", fetch = FetchType.EAGER)
private List<Employee> employees;
// Getters and setters...
}
Department
实体通过 @OneToMany
关联 Employee
实体,mappedBy
指定关联字段,fetch = FetchType.EAGER
启用立即加载。
现在可通过 getEmployees()
直接访问部门员工:
public List<Employee> getAllEmployeesByDepartment(String departmentName) {
Department department = entityManager.createQuery("SELECT d FROM Department d WHERE d.name = :name", Department.class)
.setParameter("name", "Engineering")
.getSingleResult();
return department.getEmployees();
}
⚠️ 注意:FetchType.EAGER
会在单次查询中加载所有关联数据,简单粗暴但可能引发性能问题,需根据场景选择。
7. 总结
本文探讨了 Hibernate 中获取实体列表的三种核心方式:
- ✅ JPQL:语法直观,适合简单或条件已知的查询
- ✅ Criteria API:动态构建查询,适合条件运行时确定的场景
- ✅ 命名查询:预定义可重用查询,提升代码可维护性
- ✅ 一对多关系:通过关联加载简化数据获取
选择方案时需权衡场景:JPQL 适合静态查询,Criteria API 胜在动态性,命名查询则强调复用性。实际开发中踩坑经验告诉我们,避免 N+1 查询问题至关重要。
本文示例代码可在 GitHub 获取。