在Hibernate中实现日期范围查询的三种方法
[LOADING...]
1. 概述
在大多数企业级应用中,查询特定时间范围内的数据是一项核心需求。无论是生成月度财务报表,还是筛选过去 24 小时的日志,Hibernate 都提供了多种处理此类时间查询的方法。
在本教程中,我们将探讨如何使用 HQL、Criteria API 和原生 SQL 来查询两个日期之间的记录。
2. 设置
为了演示这些操作,我们先定义一个 Order(订单)实体。虽然旧版本的 Hibernate 需要特定的日期注解,但现代版本(Hibernate 5+)可以原生处理 Java 8 的 java.time 类型:
如果我们仍在使用传统的 java.util.Date,则需要使用 @Temporal 注解来指定存储的是日期、时间还是两者皆有:
3. 使用 Hibernate 查询语言 (HQL)
HQL 是 Hibernate 中编写日期范围查询最常用的方式。它具有良好的可读性,跨数据库可移植,并且允许我们直接操作实体模型,而不是原始的 SQL 表。
3.1. 使用 BETWEEN 关键字
BETWEEN 运算符是查询特定范围内记录最直接的方法。它是两端包含的,这意味着落在 startDate 或 endDate 上的记录都会被包含在结果中:
虽然这种语法很简洁,但在处理 LocalDateTime 时会带来常见的逻辑陷阱。如果我们想获取 1 月 31 日的所有订单,但将 endDate 设置为 2024-01-31 00:00:00,查询将排除掉这一整天的大部分时间。因为该运算符仅包含到午夜边界,所以 31 日上午 10:30 或晚上 9:00 下的订单在技术上“大于”结束参数,从而被排除在结果之外。
为了使用 BETWEEN 捕获最后一天,我们必须手动将时间设置为该天的最后一毫秒(23:59:59.999)。为了避免这种脆弱的手动计算,更稳健的模式是使用比较运算符创建“半开区间”。
3.2. 使用比较运算符
当我们查询日历边界(如全天或整月)时,最安全的模式是使用半开区间:下界包含,上界不包含。这意味着我们对开始时间使用 >=,对结束时间使用 <。
假设我们想要 2024 年 1 月的所有订单。与其费力计算 1 月 31 日的 23:59:59,不如简单地使用 2 月 1 日作为排除性的 endDate:
这种模式之所以首选,是因为无论数据库存储的是微秒、毫秒还是秒,它都能正常工作。我们永远不必担心遗漏落在边界上的记录。
4. 使用 Criteria API
Criteria API 提供了一种以编程方式构建日期范围查询的方法。当我们需要构建动态搜索界面(用户可能提供开始日期、结束日期、两者都提供或都不提供)时,它特别有价值。与 HQL 字符串不同,Criteria API 是类型安全的,因此当字段名称更改时,IDE 可以帮助我们进行重构。
4.1. 使用 BETWEEN 的基本查询
对于简单的包含性查询,我们可以使用 between() 方法。与 HQL 一样,这包括正好落在 endDate 上的记录:
4.2. 使用比较运算符
当我们需要一个排除性的上界时,我们将 between() 替换为 greaterThanOrEqualTo() 和 lessThan()。这给了我们与 HQL 中相同的半开区间模式:
4.3. 构建动态查询
当某些过滤器是可选的时候,Criteria API 的优势就体现出来了。我们可以构建一个 Predicate 对象列表,并仅应用用户提供的那些条件:
这种方法保持了代码整洁,并避免了我们在处理动态 HQL 时遇到的字符串拼接麻烦。
5. 使用原生 SQL
有时我们需要使用 HQL 不支持的数据库特定日期函数,例如 PostgreSQL 的 DATE_TRUNC 或 Oracle 的 TRUNC。在这种情况下,Hibernate 允许我们回退到原生 SQL 查询。
以下是如何直接在 SQL 中编写日期范围查询。注意,我们使用的是数据库列名(如 creation_date),而不是实体字段名(如 creationDate):
对于更复杂的场景,我们可以利用特定的数据库功能。例如,如果我们想完全忽略时间部分,可以使用数据库特定的函数,如 PostgreSQL 的 DATE_TRUNC 或 MySQL 的 DATE():
虽然原生 SQL 功能强大,但应谨慎使用。我们编写的每一个原生查询都将应用程序绑定到特定的数据库。如果我们以后从 PostgreSQL 切换到 MySQL,就需要重写这些查询。通常的经验法则是:从 HQL 开始,只有在 Hibernate 的 HQL 无法表达我们的需求时,才使用原生 SQL。
6. 总结
我们介绍了在 Hibernate 中查询两个日期之间记录的三种不同方法。由于其可读性和可移植性,HQL 是大多数场景的最佳选择。Criteria API 在构建带有可选过滤器的动态查询时非常出色。原生 SQL 作为数据库特定功能的强大后盾,但我们需要注意它带来的厂商锁定问题。
对于大多数应用程序,在 HQL 中坚持使用半开区间模式(>= startDate AND < endDate)将是最简单且最可靠的方法。
一如既往,本教程的完整源代码可在 GitHub 上找到。