0

0

基于JPA/Hibernate通过关联表值进行数据筛选的教程

心靈之曲

心靈之曲

发布时间:2025-11-25 16:49:12

|

370人浏览过

|

来源于php中文网

原创

基于JPA/Hibernate通过关联表值进行数据筛选的教程

本文详细介绍了在jpa和hibernate环境中,如何根据关联表(外键关联实体)的属性值来筛选主实体数据。我们将探讨三种主要的实现方式:简洁直观的jpa jpql、类型安全且灵活的jpa criteria api,以及针对hibernate用户的传统criteria api。通过具体代码示例,本教程旨在帮助开发者理解并掌握在复杂数据关联场景下构建精确查询的技术。

在企业级应用开发中,数据模型通常包含多个相互关联的实体。当需要根据一个实体(主实体)的关联实体(外键关联)的特定属性值来检索主实体数据时,就需要构建跨越这些关联的查询。例如,从一个队列(Queue)实体中,筛选出属于特定地点(Location)和特定队列房间(QueueRoom)的所有队列。本教程将以一个Queue实体为例,演示如何高效地实现这类复合筛选。

1. 数据模型概览

假设我们有一个Queue实体,它与Location和QueueRoom实体存在多对一的关联关系:

import javax.persistence.*;

@Entity
@Table(name = "queue")
public class Queue {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "queue_id")
    private Integer queueId;

    @ManyToOne
    @JoinColumn(name = "location_id", nullable = false)
    private Location location; // 关联Location实体

    @ManyToOne
    @JoinColumn(name = "queue_room_id", nullable = true)
    public QueueRoom queueRoom; // 关联QueueRoom实体

    // Getters and Setters...
    public Integer getQueueId() { return queueId; }
    public void setQueueId(Integer queueId) { this.queueId = queueId; }
    public Location getLocation() { return location; }
    public void setLocation(Location location) { this.location = location; }
    public QueueRoom getQueueRoom() { return queueRoom; }
    public void setQueueRoom(QueueRoom queueRoom) { this.queueRoom = queueRoom; }
}

// Location 和 QueueRoom 实体结构类似,包含一个 String uuid 字段
@Entity
@Table(name = "location")
class Location {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;
    private String uuid;
    // Getters and Setters...
    public String getUuid() { return uuid; }
    public void setUuid(String uuid) { this.uuid = uuid; }
}

@Entity
@Table(name = "queue_room")
class QueueRoom {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;
    private String uuid;
    // Getters and Setters...
    public String getUuid() { return uuid; }
    public void setUuid(String uuid) { this.uuid = uuid; }
}

我们的目标是根据location的uuid和queueRoom的uuid来查询Queue列表。

2. 使用 JPA JPQL (Java Persistence Query Language)

JPQL 是一种面向对象的查询语言,它在实体和它们的属性上操作,而不是直接在数据库表和列上操作。对于关联实体的属性筛选,JPQL 提供了一种非常直观和简洁的方式。

2.1 JPQL 查询语法

要实现基于关联表值的筛选,可以直接通过点号 (.) 访问关联实体的属性。多个条件可以使用 AND 逻辑运算符连接。

SELECT q FROM Queue q
WHERE q.location.uuid = :locationUuid
AND q.queueRoom.uuid = :queueRoomUuid

在上述JPQL查询中:

  • SELECT q FROM Queue q:表示从Queue实体中选择所有Queue对象,并为其指定别名q。
  • q.location.uuid:通过q访问其关联的location实体,再访问location实体的uuid属性。
  • q.queueRoom.uuid:同理,访问queueRoom实体的uuid属性。
  • :locationUuid 和 :queueRoomUuid:是命名参数,用于在执行查询时传入具体的值。

2.2 在 Java 代码中执行 JPQL 查询

通常通过 EntityManager 来创建和执行 JPQL 查询。

import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import java.util.List;

public class QueueRepository {

    private EntityManager entityManager; // 注入或获取EntityManager实例

    public QueueRepository(EntityManager entityManager) {
        this.entityManager = entityManager;
    }

    public List getAllQueuesByLocationAndQueueRoom(String locationUuid, String queueRoomUuid) {
        String jpql = "SELECT q FROM Queue q " +
                      "WHERE q.location.uuid = :locationUuid " +
                      "AND q.queueRoom.uuid = :queueRoomUuid";

        TypedQuery query = entityManager.createQuery(jpql, Queue.class);
        query.setParameter("locationUuid", locationUuid);
        query.setParameter("queueRoomUuid", queueRoomUuid);

        return query.getResultList();
    }
}

优点:

  • 简洁易读: 查询语句与SQL类似,但操作的是实体对象,更符合面向对象思维。
  • 开发效率高: 对于固定查询,编写速度快。

缺点:

  • 字符串拼接: 如果查询条件是动态的,可能需要进行字符串拼接,容易出错且不易维护。
  • 缺乏编译时检查: JPQL语法错误只能在运行时发现。

3. 使用 JPA Criteria API

JPA Criteria API 是一种类型安全的、程序化的查询构建方式。它允许开发者通过 Java 代码构建查询,从而在编译时捕获潜在的错误,并方便地构建动态查询。

3.1 Criteria API 查询构建

import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.Predicate;
import java.util.List;

public class QueueRepository {

    private EntityManager entityManager;

    public QueueRepository(EntityManager entityManager) {
        this.entityManager = entityManager;
    }

    public List getAllQueuesByLocationAndQueueRoomCriteria(String locationUuid, String queueRoomUuid) {
        CriteriaBuilder builder = entityManager.getCriteriaBuilder();
        CriteriaQuery criteria = builder.createQuery(Queue.class);
        Root queueRoot = criteria.from(Queue.class); // 定义查询的根实体

        // 构建第一个条件:q.location.uuid = :locationUuid
        Predicate locationPredicate = builder.equal(
            queueRoot.get("location").get("uuid"), // 导航到 location 实体,再获取 uuid 属性
            locationUuid
        );

        // 构建第二个条件:q.queueRoom.uuid = :queueRoomUuid
        Predicate queueRoomPredicate = builder.equal(
            queueRoot.get("queueRoom").get("uuid"), // 导航到 queueRoom 实体,再获取 uuid 属性
            queueRoomUuid
        );

        // 使用 builder.and() 将两个条件组合起来
        criteria.where(builder.and(locationPredicate, queueRoomPredicate));

        return entityManager.createQuery(criteria).getResultList();
    }
}

代码解析:

SoftGist
SoftGist

SoftGist是一个软件工具目录站,每天为您带来最好、最令人兴奋的软件新产品。

下载
  1. CriteriaBuilder builder = entityManager.getCriteriaBuilder();:获取 CriteriaBuilder 实例,它是构建查询条件的工厂。
  2. CriteriaQuery criteria = builder.createQuery(Queue.class);:创建一个 CriteriaQuery 对象,指定查询结果类型为 Queue。
  3. Root queueRoot = criteria.from(Queue.class);:定义查询的根实体为 Queue,并为其创建 Root 对象。Root 对象代表查询的FROM子句。
  4. queueRoot.get("location").get("uuid"):通过 Root 对象的 get() 方法,可以安全地导航到关联实体及其属性。
  5. builder.equal(...):创建相等条件。
  6. builder.and(locationPredicate, queueRoomPredicate):使用 CriteriaBuilder 的 and() 方法将多个 Predicate (条件)组合起来,形成一个复合条件。
  7. criteria.where(...):将组合后的条件应用于查询。
  8. entityManager.createQuery(criteria).getResultList();:执行构建好的 Criteria 查询并获取结果列表。

优点:

  • 类型安全: 在编译时检查属性名和类型,减少运行时错误。
  • 动态查询: 易于根据不同条件动态地添加或移除查询谓词。
  • 可读性高: 对于复杂的动态查询,代码结构比拼接JPQL字符串更清晰。

缺点:

  • 冗长: 对于简单查询,代码量可能比JPQL多。
  • 学习曲线: 需要熟悉Criteria API的各种接口和方法。

4. 使用 Hibernate Criteria API (Legacy)

虽然 JPA Criteria API 是推荐的标准化方式,但对于仍在使用旧版 Hibernate 或熟悉其原生 Criteria API 的开发者,了解其实现方式也很有价值。需要注意的是,Hibernate 原生 Criteria API 在 Hibernate 5.2 之后已被标记为废弃(Deprecated),并建议使用 JPA Criteria API。

4.1 Hibernate Criteria API 查询构建

原始问题中尝试的方法非常接近,但关键在于如何正确地将条件应用到根查询,并正确地执行查询。

import org.hibernate.Session;
import org.hibernate.Criteria;
import org.hibernate.criterion.Restrictions;
import org.hibernate.sql.JoinType; // 用于指定连接类型
import java.util.List;

public class QueueRepository {

    private Session currentSession; // 注入或获取Hibernate Session实例

    public QueueRepository(Session currentSession) {
        this.currentSession = currentSession;
    }

    @SuppressWarnings("unchecked")
    public List getAllQueuesByLocationAndQueueRoomHibernateCriteria(String locationUuid, String queueRoomUuid) {
        Criteria criteria = currentSession.createCriteria(Queue.class, "q");

        // 创建别名并添加限制到根Criteria
        // 使用 createAlias 而不是 createCriteria 来避免创建子 Criteria 并确保所有限制都在根级别
        criteria.createAlias("q.location", "ql", JoinType.INNER_JOIN);
        criteria.createAlias("q.queueRoom", "qr", JoinType.INNER_JOIN);

        // 添加条件到根Criteria
        criteria.add(Restrictions.eq("ql.uuid", locationUuid));
        criteria.add(Restrictions.eq("qr.uuid", queueRoomUuid));

        // 假设有一个 includeVoidedObjects 方法,这里也加入
        // includeVoidedObjects(criteria, false); // 根据实际情况决定是否需要

        return criteria.list();
    }
}

代码解析:

  1. currentSession.createCriteria(Queue.class, "q"):创建针对 Queue 实体的根 Criteria 对象,并指定别名 q。
  2. criteria.createAlias("q.location", "ql", JoinType.INNER_JOIN);:通过 createAlias 方法为关联实体 location 创建一个别名 ql。这会在SQL查询中生成一个内部连接(INNER JOIN)。
  3. criteria.createAlias("q.queueRoom", "qr", JoinType.INNER_JOIN);:同理为 queueRoom 创建别名 qr。
  4. criteria.add(Restrictions.eq("ql.uuid", locationUuid));:将针对 location 实体 uuid 的条件添加到根 criteria 对象。
  5. criteria.add(Restrictions.eq("qr.uuid", queueRoomUuid));:将针对 queueRoom 实体 uuid 的条件添加到根 criteria 对象。
  6. criteria.list():执行查询并返回结果列表。

注意事项:

  • createAlias vs createCriteria: createAlias 用于创建关联的别名并将限制添加到父 Criteria,而 createCriteria 会返回一个新的 Criteria 实例,其限制默认只作用于该子 Criteria。在需要组合多个关联实体的条件时,通常使用 createAlias 更为直接。
  • 废弃状态: 再次强调,Hibernate 原生 Criteria API 已被废弃,新项目应优先使用 JPA Criteria API。

5. 选择合适的查询方式

  • JPQL: 适用于固定且相对简单的查询,或者当您更倾向于使用类似SQL的字符串查询时。它的可读性很好。
  • JPA Criteria API: 推荐用于需要构建动态查询、追求类型安全和在编译时捕获错误的场景。虽然代码可能更详细,但它提供了更大的灵活性和健壮性。
  • Hibernate Criteria API (Legacy): 仅当您在维护使用旧版 Hibernate 的遗留系统时才考虑使用。对于新开发,请避免使用。

6. 总结与最佳实践

在JPA/Hibernate中,根据关联表值进行筛选是常见的需求。无论是通过JPQL的简洁语法,还是JPA Criteria API的类型安全和动态构建能力,都能够有效地实现这一目标。

最佳实践:

  • 索引: 确保关联表的外键列和用于筛选的属性(如uuid)都建立了索引,以优化查询性能。
  • N+1问题: 如果查询结果需要访问关联实体的其他属性,考虑使用FETCH JOIN(JPQL)或join的FetchType(Criteria API)来避免N+1查询问题,一次性加载所有所需数据。
  • 事务管理: 确保所有数据库操作都在合适的事务中执行。
  • 错误处理: 对查询可能抛出的异常进行适当的处理。

通过掌握这些查询技术,开发者可以更灵活、高效地处理复杂的数据检索场景,构建健壮且高性能的持久层。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
数据分析工具有哪些
数据分析工具有哪些

数据分析工具有Excel、SQL、Python、R、Tableau、Power BI、SAS、SPSS和MATLAB等。详细介绍:1、Excel,具有强大的计算和数据处理功能;2、SQL,可以进行数据查询、过滤、排序、聚合等操作;3、Python,拥有丰富的数据分析库;4、R,拥有丰富的统计分析库和图形库;5、Tableau,提供了直观易用的用户界面等等。

727

2023.10.12

SQL中distinct的用法
SQL中distinct的用法

SQL中distinct的语法是“SELECT DISTINCT column1, column2,...,FROM table_name;”。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

328

2023.10.27

SQL中months_between使用方法
SQL中months_between使用方法

在SQL中,MONTHS_BETWEEN 是一个常见的函数,用于计算两个日期之间的月份差。想了解更多SQL的相关内容,可以阅读本专题下面的文章。

350

2024.02.23

SQL出现5120错误解决方法
SQL出现5120错误解决方法

SQL Server错误5120是由于没有足够的权限来访问或操作指定的数据库或文件引起的。想了解更多sql错误的相关内容,可以阅读本专题下面的文章。

1243

2024.03.06

sql procedure语法错误解决方法
sql procedure语法错误解决方法

sql procedure语法错误解决办法:1、仔细检查错误消息;2、检查语法规则;3、检查括号和引号;4、检查变量和参数;5、检查关键字和函数;6、逐步调试;7、参考文档和示例。想了解更多语法错误的相关内容,可以阅读本专题下面的文章。

360

2024.03.06

oracle数据库运行sql方法
oracle数据库运行sql方法

运行sql步骤包括:打开sql plus工具并连接到数据库。在提示符下输入sql语句。按enter键运行该语句。查看结果,错误消息或退出sql plus。想了解更多oracle数据库的相关内容,可以阅读本专题下面的文章。

821

2024.04.07

sql中where的含义
sql中where的含义

sql中where子句用于从表中过滤数据,它基于指定条件选择特定的行。想了解更多where的相关内容,可以阅读本专题下面的文章。

581

2024.04.29

sql中删除表的语句是什么
sql中删除表的语句是什么

sql中用于删除表的语句是drop table。语法为drop table table_name;该语句将永久删除指定表的表和数据。想了解更多sql的相关内容,可以阅读本专题下面的文章。

423

2024.04.29

俄罗斯Yandex引擎入口
俄罗斯Yandex引擎入口

2026年俄罗斯Yandex搜索引擎最新入口汇总,涵盖免登录、多语言支持、无广告视频播放及本地化服务等核心功能。阅读专题下面的文章了解更多详细内容。

158

2026.01.28

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 3万人学习

C# 教程
C# 教程

共94课时 | 7.8万人学习

Java 教程
Java 教程

共578课时 | 52.6万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号