Hibernate JPA生成错误Insert语句:单表继承+复合主键绑定异常
问题解决:单表继承+@IdClass复合主键时Hibernate参数绑定错误
在Spring Boot 2.7 + H2环境中,使用单表继承结合@IdClass实现复合主键时,Hibernate生成的Insert语句出现参数绑定错位:eventTime的数值被绑定到event_type列,子类的鉴别器值NEWMEMBERJOINED被绑定到updated列,直接引发数据类型转换错误。
问题根源
核心矛盾在于将鉴别器列同时标记为@Id字段且设置insertable=false:
- 父类中
eventType作为鉴别器列,被标记为insertable=false,意味着Hibernate不会主动插入该字段的值,而是期望通过子类的@DiscriminatorValue自动填充。 - 但同时
eventType又是复合主键的一部分,Hibernate在处理复合主键的参数绑定顺序时,无法正确识别这个特殊字段的逻辑,导致参数顺序与列顺序完全错位。
解决方案
方案1:移除鉴别器列的主键身份(推荐)
业务上通常不需要把鉴别器列作为主键的一部分,只需保留usid+eventTime作为复合主键,鉴别器列单独作为普通字段:
修改父类AbstractMemberEvent:
@Entity @Table(name = "MEMBER_EVENTS") @Getter @FieldDefaults(level = AccessLevel.PRIVATE) @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name = "EVENT_TYPE") @NoArgsConstructor @IdClass(MemberEventId.class) public abstract class AbstractMemberEvent { @Id @Column(name = "USID") String usid; @Id @Column(name = "UPDATED") Long eventTime; // 移除@Id标记,保留鉴别器列但不作为主键 @Column(name = "EVENT_TYPE", insertable = false, updatable = false ) String eventType; }
修改MemberEventId:
@Getter @FieldDefaults(level = AccessLevel.PRIVATE) @NoArgsConstructor @EqualsAndHashCode public class MemberEventId implements Serializable { String usid; Long eventTime; // 移除eventType字段 }
方案2:保留鉴别器列作为主键(需手动控制值)
如果业务必须将eventType作为主键一部分,需要去掉insertable=false,手动设置鉴别器值:
修改父类AbstractMemberEvent:
@Entity @Table(name = "MEMBER_EVENTS") @Getter @FieldDefaults(level = AccessLevel.PRIVATE) @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name = "EVENT_TYPE") @NoArgsConstructor @IdClass(MemberEventId.class) public abstract class AbstractMemberEvent { @Id @Column(name = "USID") String usid; @Id @Column(name = "UPDATED") Long eventTime; @Id @Column(name = "EVENT_TYPE") // 移除insertable=false String eventType; }
子类添加@PrePersist自动设置鉴别器值:
@Entity @Getter @Setter @FieldDefaults(level = AccessLevel.PRIVATE) @DiscriminatorValue("NEWMEMBERJOINED") @NoArgsConstructor public class NewMemberJoined extends AbstractMemberEvent{ @Column(name="PAYLOAD") NewMemberJoinedInfo newMemberJoinedInfo; @PrePersist void prePersist() { setEventType("NEWMEMBERJOINED"); } }
验证效果
修改后,Hibernate生成的Insert语句参数绑定会恢复正常:eventTime对应updated列,鉴别器值对应event_type列,不会再出现类型转换错误。
内容的提问来源于stack exchange,提问作者Unibit
相关产品推荐
相关产品推荐

