HQL中的正则表达式支持
HQL中的正则表达式支持
1. 概述
Hibernate 7.2 通过 like regexp 运算符在 HQL 中引入了原生正则表达式支持。在此之前,对字符串列进行复杂模式匹配需要编写原生查询。新的运算符将正则匹配委托给数据库引擎,避免了原生查询和 Java 中的后置过滤。
在本教程中,我们将通过一个简单的 Spring Boot 示例来演示 like regexp 的用法。我们将深入底层,了解 Hibernate 如何将其转换为 SQL,以及当底层数据库不支持正则表达式时会发生什么。
2. 准备一个简单的 Spring Boot 示例
在演示 like regexp 运算符之前,我们需要一个可运行查询的小型项目。我们将设置依赖项、实体、种子数据、应用程序属性和 Spring Data 仓库。为简洁起见,此处不展示所有代码。
2.1. 依赖项
我们的项目使用 Spring Boot 3.3 作为父级。Spring Boot 3.3 的 BOM 将 Hibernate 固定为 6.5,因此我们覆盖了版本以及 Hibernate 7 需要更新版本的几个传递依赖项:
除此之外,我们还引入了常规的 spring-boot-starter-data-jpa 和 H2 驱动,因为我们将使用 H2 内存数据库以便演示自包含。
2.2. 实体
我们用一个单列实体保持简洁:
2.3. 种子数据
我们通过 Spring 的 data.sql 机制加载测试数据。这些值是旨在练习正则表达式模式的短句:
2.4. 应用程序属性
测试配置文件 (application-test.yaml) 连接 H2 并要求 Spring 在 Hibernate 创建模式后运行 data.sql:
defer-datasource-initialization: true 设置确保 data.sql 在模式创建后运行。 另外值得一提的是,我们启用了 show-sql 选项来记录转换后的 SQL。这有助于我们理解 like regexp 运算符的工作原理。稍后我们将讨论这一点。
2.5. 仓库
仓库是我们将要实际运行的部分。它使用新的 like regexp / not like regexp 运算符声明了一对 HQL 查询:
与往常一样,我们将使用单元测试来演示每个运算符的行为。
3. 演示
在我们定义的仓库方法中,pattern 参数接受完整的正则表达式模式。要查找包含至少一个数字的每条消息,我们使用 .*\\d+.*:
类似地,如果我们将相同的正则表达式传递给 findByContentNotMatchingRegex() 方法,我们将获得所有不包含数字的行。
如果我们运行这两个测试,它们将通过。相同的模式参数驱动两个变体,因此我们不需要为谓词的每一侧都设置单独的正则表达式。接下来,让我们了解 Hibernate 的 like regexp 运算符在底层是如何工作的。
5. 底层原理
在了解了运算符的实际应用后,让我们看看 Hibernate 是如何实际实现它的,以及可移植性的界限在哪里。
5.1. like regexp 是如何翻译的
当 Hibernate 解析 m.content like regexp :pattern 时,它根本不会转换为 LIKE 语句。相反,它会将谓词路由到一个语义查询模型 (SQM) 函数,每个方言都以 regexp_like 的名称注册该函数。默认的 H2Dialect 将 regexp_like(x, y) 映射到 H2 的 regexp_like(x, y) SQL 函数,因此如果我们检查上述示例测试的日志输出,可以看到翻译后的 SQL:
换句话说,like regexp 是 HQL 层面的语法糖,而繁重的工作在数据库中进行。其他方言将相同的 SQM 函数绑定到它们支持的任何原生结构,例如 Oracle 中的 REGEXP_LIKE、PostgreSQL 中的 ~ 运算符、MySQL 中的 REGEXP 等。
某些方言(例如 2025 年之前的 SQL Server)根本没有原生正则表达式函数。在这种情况下,方言根本不注册 regexp_like,并且任何使用 like regexp 的 HQL 都会在 SQL 执行时失败,因为 Hibernate 会调用一个数据库不认识的函数。
5.2. 可移植性注意事项
由于每个方言都委托给数据库的原生正则表达式引擎,我们可以使用的正则表达式风味取决于底层数据库的支持。让我们看几个例子:
- H2 使用
java.util.regex(与 Perl 兼容,Matcher.find()语义——无隐式锚定)。 - PostgreSQL 使用 POSIX 正则表达式,在一些细微之处有所不同。例如,没有嵌入式
(?x)标志就没有\d速记法。 - Oracle 使用带有一些扩展的 POSIX,也不支持
\d。我们必须改用[[:digit:]]或[0-9]。 - MySQL (8.x) 使用 ICU 正则表达式,同样有其自身的特性。
因此,虽然 HQL 是可移植的,但我们输入到 like regexp 的模式通常不可移植。例如,针对 H2 测试的正则表达式在应用程序指向 Oracle 时可能会失败或表现不同。如果我们的应用程序针对多个数据库,我们应该坚持使用保守的正则表达式子集。
6. 结语
Hibernate 7.2 的 like regexp 运算符让我们能够直接在 HQL 中表达正则表达式谓词,并将它们下推到数据库,而无需诉诸原生查询。HQL 层面可跨方言移植,但实际的正则表达式风味取决于底层数据库引擎。
与往常一样,完整源代码可在 GitHub 上获取。