Spring Data JPA 3.1.2迁移后IN子句处理集合报错求助
迁移Spring Data JPA从2.7.1到3.1.2(同步升级Spring Boot 3.1.2)后,执行带有集合参数的JPQL查询时抛出类型转换异常,原查询在2.7.1版本可正常运行。
代码示例
查询方法
@Query(""" SELECT o FROM Storage o WHERE o.status = 'SUCCESS' AND ( (:referenceNumbers) IS NULL OR o.referenceNumber IN (:referenceNumbers) ) ... """) Page<Storage> findObject(ObjectFilter filter, Set<String> referenceNumbers, Iterable<Long> systemIds, Pageable pageable);
实体类定义
@Entity @Getter @Setter @RequiredArgsConstructor @EqualsAndHashCode @Accessors(chain = true) class Storage { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String referenceNumber; @Enumerated(EnumType.STRING) private Status status; }
异常信息
org.springframework.orm.jpa.JpaSystemException: Unknown wrap conversion requested: java.util.ImmutableCollections$Set12 to java.lang.String :
org.hibernate.type.descriptor.java.StringJavaType(java.lang.String)
解决方案
1. 修改JPQL空值判断逻辑
原写法(:referenceNumbers) IS NULL在Spring Data JPA 3.x中会触发类型转换错误(底层Hibernate会尝试将集合转换为单个字符串类型),需改为JPQL标准的集合空值判断方式:
@Query(""" SELECT o FROM Storage o WHERE o.status = 'SUCCESS' AND ( SIZE(:referenceNumbers) = 0 OR o.referenceNumber IN (:referenceNumbers) ) ... """)
若参数可能为null,可结合COALESCE兼容null场景:
AND ( COALESCE(SIZE(:referenceNumbers), 0) = 0 OR o.referenceNumber IN (:referenceNumbers) )
2. 统一参数前置处理
在调用查询方法前,将可能为null的集合参数转换为空集合(如Collections.emptySet()),避免JPQL中处理null集合的逻辑,简化查询语句。
3. 多数据库兼容说明
上述SIZE()函数是JPQL标准函数,在PostgreSQL、Oracle等主流数据库中均可正常运行,无需针对不同数据库编写差异化语句。
原因说明
Spring Data JPA 3.x基于Jakarta Persistence API 3.0,底层Hibernate对集合参数的解析逻辑做了严格校验。2.7.x版本中允许的(:collection) IS NULL写法,在3.x中会被解析为尝试将集合转换为单个值,从而触发类型转换异常。
内容的提问来源于stack exchange,提问作者Jakub K

