Hibernate 6.0禁用hbm.xml多列映射后,CompositeUserType场景迁移求助
Hibernate 6迁移:替代hbm.xml多列映射的解决方案(原CompositeUserType场景)
Hibernate 6.0已禁用hbm.xml中的多列映射功能,原Hibernate 5中基于CompositeUserType实现的接口类型多列映射(将DomainEvent映射到event_body和type_name两列)无法直接复用,以下是可行的迁移方案及报错分析:
方案1:改用注解驱动的自定义复合类型(推荐)
Hibernate 6优先支持注解配置,可将原CompositeUserType适配为Hibernate 6兼容的类型,通过注解替代hbm.xml的多列配置:
适配自定义复合类型
基于Hibernate 6的CompositeType接口(或继承AbstractCompositeUserType保持兼容)实现DomainEvent的序列化/反序列化逻辑,确保正确实现getColumns、nullSafeGet、nullSafeSet等核心方法。实体类添加注解配置
在StoredEvent的event字段上指定自定义复合类型及对应数据库列:public class StoredEvent { @CompositeType(DomainEventCompositeType.class) @Columns(columns = { @Column(name = "event_body"), @Column(name = "type_name") }) private DomainEvent event; @Column(name = "type_name", insertable = false, updatable = false) private String typeName; private long eventId; private Instant occurredOn; // 省略getter/setter }清理hbm.xml配置
删除原hbm.xml中<property name="event">下的<column>标签,改用上述注解完成映射。
方案2:嵌入式组件+属性转换器
若不想维护自定义复合类型,可通过嵌入式组件配合转换器实现类型映射:
定义嵌入式组件
封装数据库列对应的字段:@Embeddable public class EventData { @Column(name = "event_body") private String eventBody; @Column(name = "type_name") private String typeName; // 省略getter/setter }实现属性转换器
编写转换器完成DomainEvent与EventData的双向转换:@Converter(autoApply = false) public class DomainEventConverter implements AttributeConverter<DomainEvent, EventData> { @Override public EventData convertToDatabaseColumn(DomainEvent domainEvent) { if (domainEvent == null) return null; EventData data = new EventData(); data.setTypeName(domainEvent.getClass().getName()); data.setEventBody(serialize(domainEvent)); // 替换为你的序列化逻辑 return data; } @Override public DomainEvent convertToEntityAttribute(EventData eventData) { if (eventData == null) return null; try { Class<?> eventClass = Class.forName(eventData.getTypeName()); return deserialize(eventData.getEventBody(), eventClass); // 替换为你的反序列化逻辑 } catch (ClassNotFoundException e) { throw new RuntimeException("Failed to deserialize DomainEvent", e); } } // 补充序列化/反序列化实现 private String serialize(DomainEvent event) { // 示例:使用Jackson序列化 // return new ObjectMapper().writeValueAsString(event); } private <T extends DomainEvent> T deserialize(String body, Class<T> clazz) { // 示例:使用Jackson反序列化 // return new ObjectMapper().readValue(body, clazz); } }实体类集成组件与转换器
public class StoredEvent { @Convert(converter = DomainEventConverter.class) private EventData eventData; @Transient private DomainEvent event; @Column(name = "type_name", insertable = false, updatable = false) private String typeName; private long eventId; private Instant occurredOn; // 同步event与eventData的getter/setter public DomainEvent getEvent() { if (event == null && eventData != null) { event = new DomainEventConverter().convertToEntityAttribute(eventData); } return event; } public void setEvent(DomainEvent event) { this.event = event; this.eventData = new DomainEventConverter().convertToDatabaseColumn(event); } }
报错原因分析
- "Could not locate field name [eventBody] on class [domain.model.DomainEvent]":Hibernate 6禁用hbm.xml多列映射后,框架默认尝试从
DomainEvent接口中解析eventBody字段,导致报错,必须放弃原hbm.xml的多列写法。 - "Could not determine recommended JdbcType for domain.model.DomainEvent":纯组件方式未指定类型转换逻辑,Hibernate无法自动映射接口类型到数据库列,需配合
AttributeConverter完成转换。
内容的提问来源于stack exchange,提问作者Sylvain Lecoy
相关产品推荐
相关产品推荐

