Hibernate ORM版本6.1.0.Final
几天前刚刚发布,但公告并没有深入细节。如果你想要了解更多关于这个最新版本的一些热门新特性,请继续阅读。
FROM子句中的子查询
人们期待已久的一个特性,也称为HHH-3356,终于得到了实现!到目前为止,使用HQL和Criteria查询仅限于实体和关联,对于大多数情况来说已经足够好。但总有一些痒点,因为SQL允许在from
子句中放置子查询,而Hibernate ORM使用HQL却无法在from
子句中建模动态子查询。你最好的办法是,给实体添加@Subselect
注解,告诉Hibernate数据来自SQL子查询而不是表引用。但这既不灵活也不易于移植。JIRA问题上的投票很高,我本来也计划要完成这个工作,因为我想要添加对lateral
连接的支持。
忘记你对ORM查询的固有印象,因为Hibernate ORM即将通过引入派生from
子句节点概念来改变游戏规则。
除了常规的from
节点,这些节点在from
子句根(例如,from MyEntity e
)或关联(例如,join e.association a
)的情况下引用实体类型,或者在嵌入式或元素集合连接的情况下引用基本/嵌入式类型(例如,join e.elementCollection e
),现在又有一个新的成员。
我们将这种新的from
节点称为“派生的”,因此现在你也可以有一个派生根和派生连接。这个节点的类型是“动态的”,因为它基于其子查询的选择项,而不是Java类。
简而言之,派生子查询的每个选择项别名都成为动态类型的一部分模型。模型部分的类型是从子查询中相应的选择项表达式类型派生出来的。
如果您了解如何在SQL中操作这些内容,您会发现HQL中大部分操作都相同,只是没有隐式命名选择项。让我们考虑以下查询示例
select d.derivedId, d.derivedText
from (
select
p.id as derivedId,
p.firstname || ' ' || p.lastname as derivedText
from
Person p
) d
变量d
指的是一个派生根类型,该类型包含两个模型部分derivedId
和derivedText
。derivedId
的类型与Person#id
相同,但derivedText
的类型将是String
,因为这是concat
函数的表达式类型。
考虑到这些子查询可以包含聚合函数,这表明这不仅仅是一种封装数据子集的华丽方式,实际上它使HQL能够执行新的查询类型。
如果您对聚合不感兴趣,那么您可能更倾向于lateral
子查询?在from
子句中的子查询通常可以独立执行,这意味着它们不允许引用外部查询的别名。为了能够引用外部查询的别名,必须将一个from
子句子查询标记为lateral
,这通常等同于同名的SQL概念或其变体,如cross apply
。
考虑以下示例,以获取每个人的前三个访问位置
select p.firstname, d.nr, d.loc.street, d.loc.zip
from Person p
left join lateral (
select
row_number() over() as nr,
count(*) as visitCount,
v.location as loc
from
p.visitedLocations v
group by
v.location
order by
visitCount desc
limit 3
) d
在这个示例中,from
子句中的子查询引用了在封闭查询中定义的p
别名的visitedLocations
集合。在SQL中,这会导致查询person_visited_locations
表,并在子查询中添加一个where
子句谓词,以匹配由p
给出的“当前”人的行。
请注意,并非所有数据库都支持lateral
子查询,并且对lateral
子查询的模拟仅限于某些子查询形状。为了避免在生产中出现意外,我们特别建议您检查使用lateral
的查询是否可以在您的目标数据库上执行。
如果您认为这类似于嵌套循环连接,那么您完全正确。您可以阅读数据库文档了解如何实现横向连接的详细信息,但据我所知,横向连接将强制执行特定的连接顺序和嵌套循环连接算法。不要为此感到害怕,只是要注意其含义 ;)
正如您可能已经注意到的,一个常规的from
子句子查询非常类似于一个公共表表达式(CTE),与著名的SQL with
子句相关。现在我们已经实现了对from
子句子查询的支持,这将为模拟CTE提供基础,我想向您透露,我们将开始在下一个Hibernate ORM版本中着手实现with
子句的支持。
如果您现在感到失望,因为您理解常规CTE不会为查询表带来任何新功能,那么请放心,我们不会仅仅支持常规CTE。除了支持recursive
关键字外,Hibernate ORM还将尝试添加对不太为人所知的search
和cycle
子句的支持。
如果您想跟踪CTE支持进度,请访问HHH-15328。
JDBC数组类型支持
在Hibernate ORM 6.1之前,对Java数组的支持主要限于byte[]
/char[]
用于二进制/文本DDL类型以及将*到多关联映射为T[]
Java数组。其他类型的数组至今被视为“可序列化”的,其效果是将Java数组以标准的Java序列化方式序列化到一个二进制DDL类型。
使用二进制DDL类型的最大问题是,开发人员无法使用SQL和常用的数据库客户端“看到”或操作实际数据。为了使数据更易于访问,同时仍然将其紧密打包在单个列中,有必要利用更高级的DDL类型和编码技术。
从Hibernate ORM 6.1开始,除了之前提到的特殊数组类型(如byte[]
等)之外,基本数组以及基本集合现在是第一类公民,可以被映射到array
、json
或xml
DDL类型。实际上,默认映射已更改为根据实际数据库支持将所谓的“基本复数”类型映射到array
、json
或xml
。使用这些DDL类型允许通过相应数据类型的功能访问或操作单个元素。
利用这种新映射很简单,只需在持久化模型中声明基本类型数组即可。
@Entity
public class MyEntity {
// ...
int[] favoriteNumbers;
}
请注意,您不仅限于Java数组或原始类型。您还可以使用扩展java.util.Collection
的复数类型,如List
或Set
。
@Entity
public class MyEntity {
// ...
Set<TopicTag> watchedTags;
enum TopicTag { TOPIC1, TOPIC2 }
}
只要您不对字段使用@ElementCollection
注解,它将被视为“基本复数”映射。
Hibernate ORM 6.2或6.3可能会引入HQL访问器/操作器函数,这将允许访问/修改单个元素。使用结构可访问的DDL类型表示数据的好处在于,这些函数可以被正确地模拟。因此,即使您的数据库不支持原生数组类型,也有很大的可能性它支持JSON或XML函数,这些函数可以在幕后使用,以提供跨所有主要数据库的相同体验。
展望
在6.1.0.Final实现中,对from
子查询支持的某些限制将在6.1.1.Final中作为HHH-15330
的一部分被取消。
由于我提到了6.2或6.3的潜在功能,我想谈谈我计划为6.2做的工作,即支持将JDBC复合/结构类型映射到可嵌入类型。如果您想加入讨论以塑造这个功能,请通过在GitHub讨论中发表评论来告诉我们您的想法。
就这样,我总结了这篇关于6.1热点功能的文章!尝试使用6.1,并告诉我们您喜欢什么或什么不起作用。任何反馈都受欢迎:)