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,通过 EntityManagercreateQuery() 创建查询对象,设置结果类型为 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 获取。


原始标题:Get List of Entity From Database in Hibernate