Hibernate 6.2为何将复合主键的JPQL等于操作转为IN子查询?
问题背景
我有一个基于Spring Boot 3.1.1 / Hibernate 6.2的应用,使用以下依赖的ojdbc8驱动连接Oracle 12c数据库:
<dependency> <groupId>com.oracle.database.jdbc</groupId> <artifactId>ojdbc8</artifactId> <scope>runtime</scope> </dependency>
该应用包含如下仓库接口与实体类:
public interface SomeEntityRepository extends Repository<SomeEntity, SomeEntityId> { @Query(""" FROM SomeEntity WHERE id = :id AND description is not null """) Optional<SomeEntity> find(SomeEntityId id); }
@Embeddable public class SomeEntityId { private Long idX; private Long idY; private Long idZ; }
@Entity public class SomeEntity { @EmbeddedId private SomeEntityId id; private String description; }
执行find查询时,Hibernate 6.2将JPQL翻译为如下SQL:
Hibernate: select s1_0.idx, s1_0.idy, s1_0.idz, s1_0.description from some_entity s1_0 where ( s1_0.idx,s1_0.idy,s1_0.idz ) in (select ?,?,? from dual) and s1_0.description is not null
而在Spring Boot 2.7.13 / Hibernate 5.6环境中执行相同代码,JPQL会被翻译为逐个字段比较的SQL:
Hibernate: select someentity0_.idx as idx1_1_, someentity0_.idy as idy2_1_, someentity0_.idz as idz3_1_, someentity0_.description as description4_1_ from some_entity someentity0_ where someentity0_.idx=? and someentity0_.idy=? and someentity0_.idz=? and ( someentity0_.description is not null )
请问为何Hibernate 6.2会将复合主键的JPQL等于操作转为IN子查询,而非像Hibernate 5那样逐个字段比较?
原因分析
这是Hibernate 6.x为适配**SQL标准中的行值表达式(Row Value Expression)**所做的查询优化与语法调整,具体原因如下:
- 对齐SQL标准:行值表达式(如
(col1, col2, col3) = (val1, val2, val3))是SQL官方标准支持的语法,Hibernate 6.x优先生成贴合标准的SQL,替代了Hibernate 5.x时代拆分复合主键逐个字段比较的自定义逻辑。 - 兼容Oracle特性:虽然Oracle 12c本身支持直接的行值相等比较,但Hibernate 6.x考虑到部分Oracle版本或驱动的兼容性问题,选择用
IN子查询+dual表的方式模拟行值比较——这种写法在全版本Oracle中都能稳定运行,避免了直接使用行值表达式可能出现的兼容性报错。 - 统一查询翻译逻辑:Hibernate 6.x重构了核心查询翻译引擎,针对复合主键的相等判断,统一采用行值表达式的思路处理,不管是单个主键还是批量主键查询,翻译逻辑更一致,减少了不同场景下的语法分支,降低了维护成本。
需要说明的是,这种SQL写法的差异不会影响查询结果,Oracle的查询优化器通常能将IN子查询的写法优化到和逐个字段比较相同的执行计划,性能上不会有明显差异。
内容的提问来源于stack exchange,提问作者Alexandre Almeida
相关产品推荐
相关产品推荐

