Spring Boot 2迁3时@EmbeddedId复合主键JPA查询异常问题
这个问题本质是Spring Boot 3(搭配Hibernate 6)对复合主键IN查询的SQL生成逻辑发生了变更,具体原因如下:
依赖版本升级带来的规范适配
Spring Boot 3对应Spring Data JPA 3.x和Hibernate 6.x,这两个版本全面适配JPA 3.0规范,其中包含了对**行值表达式(Row Value Expressions)**的支持。Hibernate 6默认会将复合主键的findAllById查询转换为行值表达式语法,比如生成:WHERE (e.param1, e.param2) IN ((?, ?), (?, ?))你看到的错误语法(
e.param1,e.param2 IN (?,?))是行值表达式的简化输出形式,本质是Hibernate尝试用新语法匹配复合主键集合。老版本数据库兼容性问题
之前在Spring Boot 2+Hibernate 5环境下正常,是因为Hibernate 5不支持行值表达式,会自动将复合主键拆解为多个AND条件的SQL。但部分老版本数据库(比如MySQL 5.x、某些低版本PostgreSQL)不支持JPA 3.0的行值表达式语法,导致SQL执行报错。
解决办法
可以根据你的数据库环境选择以下方案:
方案一:升级数据库版本
若条件允许,将数据库升级到支持行值表达式的版本(比如MySQL 8.0+、PostgreSQL 9.4+),直接适配Hibernate 6的新语法。方案二:回退Hibernate的SQL生成逻辑
在application.properties或application.yml中添加配置,强制Hibernate使用旧的复合主键拆解方式:spring.jpa.properties.hibernate.query.legacy_row_value_expression_handler=true配置后Hibernate会回到Hibernate 5的行为,生成
e.param1 = ? AND e.param2 = ?的条件组合。方案三:自定义Repository查询方法
手动编写JPQL查询,明确指定复合主键的匹配逻辑,例如:public interface EntityRepository extends JpaRepository<Entity, Pk> { @Query("SELECT e FROM Entity e WHERE (e.id.param1, e.id.param2) IN :ids") List<Entity> findAllByCompositeIds(@Param("ids") Iterable<Pk> ids); }若数据库不支持行值表达式,也可以拆解为多个
OR条件(适合主键数量不多的场景):@Query("SELECT e FROM Entity e WHERE " + "(:ids IS NULL OR " + " (e.id.param1 = (SELECT p.param1 FROM :ids p WHERE p.param2 = e.id.param2) AND e.id.param2 IN (SELECT p.param2 FROM :ids p)))") List<Entity> findAllByCompositeIds(@Param("ids") Iterable<Pk> ids);
内容的提问来源于stack exchange,提问作者Bartun

