Hibernate 6查询PostgreSQL枚举时错误使用Java枚举类名问题
解决Hibernate 6 + PostgreSQL 15自定义查询枚举参数的SQL类型转换问题
问题根源
单条增改查正常,但自定义Repository查询时,Hibernate会将枚举参数转换为Java枚举类名(如'APPLE'::FruitType)而非数据库定义的枚举类型名(some_enum_type_name),导致PostgreSQL抛出类型不匹配异常。这是因为Hibernate6在JPQL参数绑定阶段,默认使用Java枚举类名作为类型转换标识,忽略了实体字段上配置的columnDefinition。
解决方案
方案1:自定义JdbcType绑定数据库枚举名
重写Hibernate的PostgreSQL枚举处理类,强制返回数据库枚举类型名:
- 实现自定义JdbcType:
import org.hibernate.type.descriptor.jdbc.PostgreSQLEnumJdbcType; import org.hibernate.type.descriptor.MappingTypeResolutionContext; import org.hibernate.type.descriptor.WrapperOptions; import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Types; public class DatabaseEnumJdbcType extends PostgreSQLEnumJdbcType { private final String dbEnumName; public DatabaseEnumJdbcType(String dbEnumName) { this.dbEnumName = dbEnumName; } @Override public String getCastTypeName(int sqlType, MappingTypeResolutionContext context) { // 返回数据库枚举类型名,替代默认的Java枚举类名 return dbEnumName; } @Override public void nullSafeSet(PreparedStatement st, Object value, int index, WrapperOptions options) throws SQLException { if (value == null) { st.setNull(index, Types.OTHER); } else { // 直接传入枚举的字符串值,PostgreSQL自动匹配对应枚举类型 st.setObject(index, ((Enum<?>) value).name(), Types.OTHER); } } }
- 在实体字段上配置该JdbcType:
import jakarta.persistence.*; import org.hibernate.annotations.JdbcType; import org.hibernate.annotations.JdbcTypeParameter; @Entity @Table(name = "foo") public class FooEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Enumerated(EnumType.STRING) @Column(name = "fruit_type", columnDefinition = "some_enum_type_name") @JdbcType(value = DatabaseEnumJdbcType.class, parameters = @JdbcTypeParameter(name = "dbEnumName", value = "some_enum_type_name")) private FruitType fruitType; // 构造器、getter、setter省略 }
此方案一劳永逸,所有涉及该枚举的查询都会自动使用正确的数据库类型转换。
方案2:JPQL查询中显式CAST类型
如果不想自定义JdbcType,可在JPQL语句中手动将枚举参数转换为数据库枚举类型:
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import java.util.List; public interface FooRepository extends JpaRepository<FooEntity, Long> { @Query("SELECT f FROM FooEntity f WHERE f.fruitType = CAST(:fruitType AS some_enum_type_name)") List<FooEntity> findByFruitType(@Param("fruitType") FruitType fruitType); }
注意:some_enum_type_name必须与数据库中创建的枚举类型名完全一致。
方案3:使用原生SQL查询
若JPQL的CAST方式不生效,可改用原生SQL,手动传入枚举的字符串值并指定类型转换:
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import java.util.List; public interface FooRepository extends JpaRepository<FooEntity, Long> { @Query(value = "SELECT * FROM foo WHERE fruit_type = :fruitVal::some_enum_type_name", nativeQuery = true) List<FooEntity> findByFruitType(@Param("fruitVal") String fruitVal); }
调用时传入枚举的字符串值:
List<FooEntity> apples = fooRepository.findByFruitType(FruitType.APPLE.name());
内容的提问来源于stack exchange,提问作者k31
相关产品推荐
相关产品推荐

