Hibernate插入关联子实体时生成SQL参数顺序错误问题求助
问题原因
1. 复合主键@IdClass配置错误
你在AbstractChild类上标注的@IdClass(Child.class)存在基础错误:你的项目中并没有定义Child这个复合主键类。Hibernate处理复合主键的参数绑定顺序时,会严格按照@IdClass中定义的字段顺序排序,错误的主键类配置会直接导致参数顺序计算逻辑混乱,这是你出现参数错位的核心原因。
2. 主键字段配置冲突
你将mediaType(对应数据库type字段)同时配置了三个互斥的规则:
- 标记为
@Id,属于复合主键的组成部分,要求插入时必须赋值 - 加上了
insertable = false, updatable = false属性,要求Hibernate插入/更新时忽略该字段 - 同时它还是单表继承的判别列,由Hibernate自动根据子类的
@DiscriminatorValue赋值
三者的冲突会导致Hibernate在计算插入字段顺序、参数占位符对应关系时出现逻辑错误,你现在看到的father_id和type参数绑定错位就是这个原因导致的。
3. 额外配置校验(可选)
你定义AbstractChild的表名为child,但实际生成的插入SQL操作的是tourist_informations表,如果不是粘贴失误,需要检查是否有全局表名前缀、命名策略等配置覆盖了实体定义的表名。
解决方法
第一步:创建合法的复合主键类
自定义符合JPA规范的主键类,字段名称、类型要和实体中@Id标记的字段完全对应,且实现Serializable接口、无参构造、equals和hashCode方法:
public class ChildId implements Serializable { // 字段顺序建议和数据库主键定义的顺序(father_id, type, locale)保持一致 private Father father; private short mediaType; private Locale locale; public ChildId() {} public ChildId(Father father, short mediaType, Locale locale) { this.father = father; this.mediaType = mediaType; this.locale = locale; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ChildId childId = (ChildId) o; return mediaType == childId.mediaType && Objects.equals(father, childId.father) && Objects.equals(locale, childId.locale); } @Override public int hashCode() { return Objects.hash(father, mediaType, locale); } }
然后把AbstractChild上的@IdClass注解修改为@IdClass(ChildId.class)。
第二步:修复主键字段冲突
如果确实需要把判别列type加入复合主键,直接删除mediaType字段上的insertable = false, updatable = false属性即可,Hibernate会自动将判别列的值同步到mediaType字段,不会出现插入空值的问题:
@Id @Column(name = "type") protected short mediaType;
如果不需要把type作为主键的一部分,直接删除mediaType字段上的@Id注解即可,判别列本身不需要手动映射为实体字段。
修改完成后重新测试,Hibernate生成的插入SQL参数顺序就会和字段顺序匹配了。
内容的提问来源于stack exchange,提问作者Luca Farsetti

