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

基于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 qWHERE q.location.uuid = :locationUuidAND 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();    }}

代码解析:

稿定抠图 稿定抠图

AI自动消除图片背景

稿定抠图 76 查看详情 稿定抠图 CriteriaBuilder builder = entityManager.getCriteriaBuilder();:获取 CriteriaBuilder 实例,它是构建查询条件的工厂。CriteriaQuery criteria = builder.createQuery(Queue.class);:创建一个 CriteriaQuery 对象,指定查询结果类型为 Queue。Root queueRoot = criteria.from(Queue.class);:定义查询的根实体为 Queue,并为其创建 Root 对象。Root 对象代表查询的FROM子句。queueRoot.get(“location”).get(“uuid”):通过 Root 对象的 get() 方法,可以安全地导航到关联实体及其属性。builder.equal(…):创建相等条件。builder.and(locationPredicate, queueRoomPredicate):使用 CriteriaBuilder 的 and() 方法将多个 Predicate (条件)组合起来,形成一个复合条件。criteria.where(…):将组合后的条件应用于查询。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();    }}

代码解析:

currentSession.createCriteria(Queue.class, “q”):创建针对 Queue 实体的根 Criteria 对象,并指定别名 q。criteria.createAlias(“q.location”, “ql”, JoinType.INNER_JOIN);:通过 createAlias 方法为关联实体 location 创建一个别名 ql。这会在SQL查询中生成一个内部连接(INNER JOIN)。criteria.createAlias(“q.queueRoom”, “qr”, JoinType.INNER_JOIN);:同理为 queueRoom 创建别名 qr。criteria.add(Restrictions.eq(“ql.uuid”, locationUuid));:将针对 location 实体 uuid 的条件添加到根 criteria 对象。criteria.add(Restrictions.eq(“qr.uuid”, queueRoomUuid));:将针对 queueRoom 实体 uuid 的条件添加到根 criteria 对象。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查询问题,一次性加载所有所需数据。事务管理: 确保所有数据库操作都在合适的事务中执行。错误处理: 对查询可能抛出的异常进行适当的处理。

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

以上就是基于JPA/Hibernate通过关联表值进行数据筛选的教程的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1077461.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
如何在css中使用多类组合选择器
上一篇 2025年12月2日 07:24:02
Safari浏览器视频投屏失败如何解决 Safari与AirPlay连接排查
下一篇 2025年12月2日 07:24:06

相关推荐

  • 修复Django电商项目中AJAX过滤产品列表图片不显示问题

    在Django电商项目中,当使用AJAX动态加载过滤后的产品列表时,常遇到图片无法正常显示的问题。这通常是由于前端模板中图片加载方式(如data-setbg属性结合JavaScript库)与AJAX动态内容更新机制不兼容所致。解决方案是直接在AJAX返回的HTML中使用标准的标签来渲染图片,确保浏览…

    2026年5月10日
    700
  • Matplotlib 地图中多类型图例的创建与优化

    Matplotlib 地图中多类型图例的创建与优化Matplotlib 地图中多类型图例的创建与优化Matplotlib 地图中多类型图例的创建与优化Matplotlib 地图中多类型图例的创建与优化

    本教程旨在解决matplotlib地图可视化中,如何在一个图例中同时展示颜色块(如区域分类)和自定义标记(如特定兴趣点)的问题。文章详细介绍了当传统`patch`对象无法正确显示标记时,如何利用`matplotlib.lines.line2d`创建标记图例句柄,并将其与颜色块图例句柄合并,从而生成一…

    2026年5月10日 用户投稿
    900
  • RichHandler与Rich Progress集成:解决显示冲突的教程

    在使用rich库的`richhandler`进行日志输出并同时使用`progress`组件时,可能会遇到显示错乱或溢出问题。这通常是由于为`richhandler`和`progress`分别创建了独立的`console`实例导致的。解决方案是确保日志处理器和进度条组件共享同一个`console`实例…

    2026年5月10日
    300
  • 修复点击时按钮抖动:CSS垂直对齐实践

    本文探讨了在Web开发中,交互式按钮(如播放/暂停按钮)在点击时发生意外垂直位移的问题。通过分析CSS样式变化对元素布局的影响,我们发现这是由于按钮不同状态下的边框样式和内边距改变,以及默认的垂直对齐行为共同作用所致。核心解决方案是利用CSS的vertical-align属性,将其设置为middle…

    2026年5月10日
    100
  • 使用 Jupyter Notebook 进行探索性数据分析

    Jupyter Notebook通过单元格实现代码与Markdown结合,支持数据导入(pandas)、清洗(fillna)、探索(matplotlib/seaborn可视化)、统计分析(describe/corr)和特征工程,便于记录与分享分析过程。 Jupyter Notebook 是进行探索性…

    2026年5月10日
    000
  • 如何在HTML中插入表单元素_HTML表单控件与输入类型使用指南

    HTML表单通过标签构建,包含action和method属性定义数据提交目标与方式,常用input类型如text、password、email等适配不同输入需求,配合label、required、placeholder提升可用性,结合textarea、select、button等控件实现完整交互,是…

    2026年5月10日
    300
  • 前端缓存策略与JavaScript存储管理

    根据数据特性选择合适的存储方式并制定清晰的读写与清理逻辑,能显著提升前端性能;合理运用Cookie、localStorage、sessionStorage、IndexedDB及Cache API,结合缓存策略与定期清理机制,可在保证用户体验的同时避免安全与性能隐患。 前端缓存和JavaScript存…

    2026年5月10日
    200
  • HTML5网页如何实现手势操作 HTML5网页移动端交互的处理技巧

    首先利用原生touch事件实现滑动判断,再通过preventDefault解决滚动冲突,接着引入Hammer.js处理复杂手势,最后通过优化点击区域、避免事件冲突和增加视觉反馈提升体验。 在移动端浏览器中,HTML5网页可以通过触摸事件实现手势操作,提升用户体验。虽然原生JavaScript提供了基…

    2026年5月10日
    000
  • 使用 WebCodecs VideoDecoder 实现精确逐帧回退

    本文档旨在解决在使用 WebCodecs VideoDecoder 进行视频解码时,实现精确逐帧回退的问题。通过比较帧的时间戳与目标帧的时间戳,可以避免渲染中间帧,从而提高用户体验。本文将提供详细的解决方案和示例代码,帮助开发者实现精确的视频帧控制。 在使用 WebCodecs VideoDecod…

    2026年5月10日
    300
  • JavaScript 闭包:理解闭包原理与内存泄漏问题

    闭包是函数访问其外部作用域变量的能力,即使外部函数已执行完毕。如 inner 函数引用 outer 中的 count,形成闭包,使变量持久存在。闭包本身无害,但可能因延长变量生命周期导致内存泄漏,例如事件监听器引用大对象时。若未及时清理 DOM 事件或定时器,闭包会阻止垃圾回收,造成内存占用过高。解…

    2026年5月10日
    100
  • JavaScript 动态菜单点击高亮效果实现教程

    本教程详细介绍了如何使用 JavaScript 实现动态菜单的点击高亮功能。通过事件委托和状态管理,当用户点击菜单项时,被点击项会高亮显示(绿色),同时其他菜单项恢复默认样式(白色)。这种方法避免了不必要的DOM操作,提高了性能和代码可维护性,确保了无论点击方向如何,功能都能稳定运行。 动态菜单高亮…

    2026年5月10日
    200
  • html5怎么画实线_HTML5用CSS border-style:solid画元素实线边框【绘制】

    可通过CSS的border-style属性设为solid添加实线边框:一、内联样式用border:2px solid #000;二、内部样式表统一设置如div{border:1px solid #333};三、外部CSS文件定义.my-box{border:3px solid red}并引入;四、单…

    2026年5月10日
    400
  • JavaScript函数中插入加载动画(Spinner)的正确方法

    本文旨在解决在JavaScript函数中插入加载动画(Spinner)时遇到的异步问题。通过引入async/await和Promise.all,确保在数据处理完成前后正确显示和隐藏加载动画,提升用户体验。我们将提供两种实现方案,并详细解释其原理和优势。 在Web开发中,当执行耗时操作时,显示加载动画…

    2026年5月10日
    500
  • JS如何实现迭代器?迭代器协议

    JavaScript中实现迭代器需遵循可迭代协议和迭代器协议,通过定义[Symbol.iterator]方法返回具备next()方法的迭代器对象,从而支持for…of和展开运算符;该机制统一了数据结构的遍历接口,实现惰性求值,适用于自定义对象、树、图及无限序列等复杂场景,提升代码通用性与…

    2026年5月10日
    300
  • 使用 Pydantic v2 实现条件性必填字段

    本文介绍了如何在 Pydantic v2 模型中实现条件性必填字段。通过自定义验证器,可以根据模型中其他字段的值来动态地控制某些字段是否为必填项,从而满足 API 交互中数据验证的复杂需求。本文提供了一个具体的示例,展示了如何确保模型中至少有一个字段被赋值。 在 Pydantic v2 中,虽然没有…

    2026年5月10日
    000
  • 动态更新圆形进度条:JavaScript成绩计算器集成指南

    本文档旨在指导开发者如何将JavaScript成绩计算系统与动态圆形进度条集成,实现可视化展示平均成绩。我们将详细讲解如何修改现有的JavaScript代码,使其在计算出平均分后,能够动态更新圆形进度条的进度,从而提供更直观的用户体验。本文档包含详细的代码示例和注意事项,帮助开发者轻松实现这一功能。…

    2026年5月10日
    000
  • React组件中动态属性值的管理与同步:利用状态实现受控组件

    本教程旨在解决react组件中动态属性值同步使用的问题。我们将探讨如何利用react的`usestate` hook来管理组件内部状态,从而实现一个属性的值动态地影响另一个属性,并构建出可预测、易于维护的受控组件。文章将通过具体代码示例,详细阐述从初始化状态到处理状态更新的完整过程,并强调受控组件在…

    2026年5月10日
    000
  • 如何讲html和css_讲解HTML与CSS结合使用基础【基础】

    需将HTML与CSS结合使用以实现网页结构与样式的分离:HTML定义标题、段落等语义结构,CSS控制颜色、字体等外观;可通过内联样式、内部样式表或外部CSS文件引入样式,并利用类选择器和ID选择器精准应用。 如果您希望网页不仅展示内容,还能具备基本的样式和结构布局,则需要将HTML与CSS结合使用。…

    2026年5月10日
    100
  • JavaScript计算器开发:解决数值显示与初始化问题

    本教程深入探讨了使用JavaScript构建计算器时常见的数值显示异常问题,特别是由于类属性未初始化导致的`Cannot read properties of undefined`错误。我们将详细分析问题根源,并通过在构造函数中调用初始化方法来解决该问题,同时优化显示逻辑,确保计算器功能稳定且界面显…

    2026年5月10日
    000
  • 高通预热 2023 骁龙峰会:以AI为主题,10 月 25-26 日举行

    高通预热 2023 骁龙峰会:以AI为主题,10 月 25-26 日举行高通预热 2023 骁龙峰会:以AI为主题,10 月 25-26 日举行高通预热 2023 骁龙峰会:以AI为主题,10 月 25-26 日举行高通预热 2023 骁龙峰会:以AI为主题,10 月 25-26 日举行

    【环球网科技综合报道】10月17日消息,高通今日对 2023 骁龙峰会进行了预热,本次大会将以 %ign%ignore_a_1%re_a_1% 为主题,届时骁龙 8 gen 3 处理器也很大可能在本届峰会亮相。 在临近活动召开之日,相关业内人士也透露了高通骁龙8Gen3跑分及规格。据悉,高通骁龙8 …

    2026年5月10日 用户投稿
    000

发表回复

登录后才能评论
关注微信