在Hibernate 6.3中使用PostgreSQLEnumJdbcType的查询问题
问题解决:Spring Boot + Hibernate 原生查询中PostgreSQL枚举参数映射错误
问题场景
使用Spring Boot v3.2.0和Hibernate v6.3.1,实体类中枚举字段已通过@Enumerated(EnumType.STRING)和@JdbcType(PostgreSQLEnumJdbcType.class)映射为PostgreSQL的ENUM类型(request_statuses):
enum STATUS { DRAFT, NEW, IN_PROGRESS, COMPLETED, REJECTED; } @Enumerated(EnumType.STRING) @Column(name = "status") @JdbcType(PostgreSQLEnumJdbcType.class) private STATUS status;
但在Repository的原生查询方法中传入枚举列表时报错:
@Query(nativeQuery = true, value = "SELECT * FROM requests r WHERE r.status IN (?1)") List<Request> getRequestsByStatus(List<Request.STATUS> statuses);
错误信息:
Caused by: org.hibernate.exception.SQLGrammarException: JDBC exception executing SQL [SELECT * FROM requests r WHERE r.status IN (?,?,?,?,?)] [ERROR: operator does not exist: request_statuses = smallint
原因分析
Hibernate处理原生查询的枚举参数时,会调用createEnumType方法创建枚举类型映射,该方法默认使用TypeConfiguration的全局默认配置(通常为ORDINAL,即枚举的序号),不会读取实体字段上的@JdbcType或@Enumerated注解,导致参数被转为小整数类型,与PostgreSQL的ENUM类型不匹配。
解决方案
方案1:全局配置枚举默认映射为STRING
在application.properties中添加全局配置,强制Hibernate将枚举默认按字符串处理:
spring.jpa.properties.hibernate.type.preferred_enum_jdbc_type=STRING
或YAML格式:
spring: jpa: properties: hibernate: type: preferred_enum_jdbc_type: STRING
此方案最简单,适合所有枚举都需要按字符串映射的场景。
方案2:在查询中显式指定参数类型
通过Hibernate的@Parameter注解,给原生查询的参数指定对应的JdbcType:
import org.hibernate.type.descriptor.jdbc.PostgreSQLEnumJdbcType; import org.hibernate.annotations.Parameter; @Query(nativeQuery = true, value = "SELECT * FROM requests r WHERE r.status IN (?1)") List<Request> getRequestsByStatus( @Parameter(name = "statuses", type = PostgreSQLEnumJdbcType.class) List<Request.STATUS> statuses );
该方案针对单个查询方法生效,适合局部调整的场景。
方案3:全局注册自定义枚举类型映射
创建自定义的EnumJavaType,强制返回字符串类型的JdbcType:
import org.hibernate.type.descriptor.java.EnumJavaType; import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.jdbc.VarcharJdbcType; public class StringEnumJavaType<E extends Enum<E>> extends EnumJavaType<E> { public StringEnumJavaType(Class<E> enumClass) { super(enumClass); } @Override public JdbcType getRecommendedJdbcType(org.hibernate.type.descriptor.java.JdbcTypeIndicators indicators) { return VarcharJdbcType.INSTANCE; } }
然后创建TypeContributor注册该类型:
import org.hibernate.boot.model.TypeContributions; import org.hibernate.service.ServiceRegistry; import org.hibernate.type.TypeContributor; public class StringEnumTypeContributor implements TypeContributor { @Override public void contribute(TypeContributions typeContributions, ServiceRegistry serviceRegistry) { typeContributions.contributeJavaType(EnumJavaType.class, (descriptor, scope) -> { Class<?> rawType = descriptor.getJavaType(); if (Enum.class.isAssignableFrom(rawType) && rawType != Enum.class) { //noinspection unchecked return new StringEnumJavaType<>((Class<? extends Enum>) rawType); } return null; }); } }
最后在配置文件中指定TypeContributor:
spring.jpa.properties.hibernate.type_contributors=com.yourpackage.StringEnumTypeContributor
此方案适合需要对枚举映射做全局定制的场景。
内容的提问来源于stack exchange,提问作者sky
相关产品推荐
相关产品推荐

