Spring Boot 2.X迁移3.1.0:Hibernate枚举注解适配问题求助
Spring Boot 2.X 迁移至3.1.0的Hibernate枚举适配方案
问题背景
将Spring Boot从2.X版本迁移到3.1.0时,遇到Hibernate注解废弃/移除的问题,原有代码中依赖的@TypeDef、带type参数的@Type注解,以及继承org.hibernate.type.EnumType的自定义枚举类型实现均无法适配新版本。原有核心代码如下:
@Entity @Table(name = "foo_event", schema = "foo") @org.hibernate.annotations.TypeDef(name = "enum_type", typeClass = PostgreSQLEnumType::class) data class FooEvent( @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", nullable = false) var id: Long? = null, @Enumerated(EnumType.STRING) @Column(name = "event_source") @org.hibernate.annotations.Type(type = "enum_type") val eventSource: EventSource ) class PostgreSQLEnumType : org.hibernate.type.EnumType<EventSource>() { @Throws(HibernateException::class, SQLException::class) override fun nullSafeSet( ps: PreparedStatement, obj: Any?, index: Int, session: SharedSessionContractImplementor? ) { if (obj == null) { ps.setNull(index, Types.OTHER) } else { ps.setObject(index, obj.toString(), Types.OTHER) } } } enum class EventSource { SOME_SOURCE, ANOTHER_SOURCE }
当前核心问题:
@TypeDef已被移除@Type注解不再支持type参数org.hibernate.type.EnumType已废弃,且无nullSafeSet方法,替换为Jakarta的EnumType无法指定自定义类型
同时疑惑:已有@Enumerated(EnumType.STRING)时,原@Type注解是否冗余?
适配方案
1. 用@Converter替代@TypeDef和@Type
Hibernate 6+(Spring Boot 3.x对应版本)推荐使用JPA标准的@Converter处理自定义类型映射,完全替代原有的TypeDef和Type注解,兼容性更强。
步骤1:实现AttributeConverter
替换原PostgreSQLEnumType,根据存储需求选择对应实现:
场景1:枚举以字符串形式存储(通用方案)
import jakarta.persistence.AttributeConverter import jakarta.persistence.Converter @Converter(autoApply = true) // autoApply=true会自动应用到所有EventSource类型字段 class EventSourceConverter : AttributeConverter<EventSource, String> { // 枚举转数据库存储值 override fun convertToDatabaseColumn(attribute: EventSource?): String? { return attribute?.name } // 数据库值转枚举 override fun convertToEntityAttribute(dbData: String?): EventSource? { return dbData?.let { EventSource.valueOf(it) } } }
场景2:适配PostgreSQL原生枚举类型
如果数据库中event_source字段是PostgreSQL原生枚举类型(而非varchar),需要调整转换器以Types.OTHER类型传递参数:
import jakarta.persistence.AttributeConverter import jakarta.persistence.Converter import java.sql.Types @Converter(autoApply = true) class PostgreSQLEventSourceConverter : AttributeConverter<EventSource, Any> { override fun convertToDatabaseColumn(attribute: EventSource?): Any? { return attribute?.name?.let { value -> // 包装为PostgreSQL原生枚举需要的类型 object : org.hibernate.type.descriptor.jdbc.JdbcType { override fun getJdbcTypeCode(): Int = Types.OTHER override fun <X> wrap(value: Any?, options: org.hibernate.type.descriptor.WrapperOptions?): X? { @Suppress("UNCHECKED_CAST") return value as X? } }.wrap(value, null) } } override fun convertToEntityAttribute(dbData: Any?): EventSource? { return dbData?.toString()?.let { EventSource.valueOf(it) } } }
步骤2:修改实体类注解
移除@TypeDef和@Type注解,根据转换器配置调整:
import jakarta.persistence.* @Entity @Table(name = "foo_event", schema = "foo") data class FooEvent( @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", nullable = false) var id: Long? = null, @Column(name = "event_source", nullable = false) // 若转换器autoApply=false,需手动指定@Convert // @Convert(converter = EventSourceConverter::class) val eventSource: EventSource )
2. 关于@Enumerated与@Type的冗余问题
原代码中@Enumerated(EnumType.STRING)和@Type(type = "enum_type")确实冗余:
@Enumerated(EnumType.STRING)是JPA标准注解,指定枚举以字符串形式存储@Type是Hibernate扩展注解,用于指定自定义类型实现,两者同时存在会导致冲突(Hibernate优先使用@Type)
新版本中:
- 若用
@Converter且autoApply=true,无需额外注解 - 若仅需字符串存储枚举,直接保留
@Enumerated(EnumType.STRING)即可,无需@Type - 若适配PostgreSQL原生枚举,使用
@Converter处理,无需@Enumerated
3. 废弃org.hibernate.type.EnumType的替代方案
若不想使用@Converter,可使用Hibernate 6+的EnumJavaType和JdbcType自定义枚举类型,但推荐优先使用JPA标准的@Converter,兼容性更好,减少对Hibernate特定API的依赖。
内容的提问来源于stack exchange,提问作者enccne
相关产品推荐
相关产品推荐

