JPA 在 MySQLIntegrityConstraintViolationException 中插入父/子结果

2023-11-24

这个问题已经被问过很多次了,但我没有找到任何好的答案,所以我会再问一次。

我的父子单向关系如下:

@Entity
@Table(name = "PARENT")
public class Parent {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ID")
    private Long parentId;

    @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST)
    @JoinTable(name = "CHILD", joinColumns = @JoinColumn(name = "parent_id"), inverseJoinColumns = @JoinColumn(name = "ID"))
    private List<Child> children;
}

@Entity
@Table(name = "CHILD")
public class Child {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ID")
    private Long id;

    @Column(name = "PARENT_ID")
    private Long parentId;

    //some other field
}

我创建一个父实例,为其分配一个子列表并尝试保留它:

Parent p = new Parent();
List<Child> children = new ArrayList<Child>();
Child c = new Child();
children.add(c);
p.addChildren(children);
em.merge(p);

当代码运行时,我得到以下异常:

MySQLIntegrityConstraintViolationException:无法添加或更新 子行:外键约束失败 (testdb.child, 约束parent_fk外键(parent_id) 参考parent (id) 在 删除无操作更新无操作)

我假设这是由于尝试插入子项时父项未完全插入的事实。

如果我不将子级添加到父级,则可以很好地插入父级。 我尝试切换 GenerationValue 生成策略,但这没有帮助。

有什么想法如何同时插入父母和孩子吗?

Edit:即使我首先坚持父级,我仍然会遇到相同的错误。我确定这是因为 child_id 没有设置;子项是默认构造的,因此parent_id设置为0,该值不存在,因此外键约束验证。

有没有办法让jpa自动设置分配给父实例的子实例的parent_id?


你们的关系不一定是双向的。这里的评论中有一些错误信息。

您还说您已将字段“parentId”添加到 Child 实体中,因为您假设 JPA 需要“了解”父字段,以便它可以设置值。根据您提供的注释,问题不在于 JPA 不了解该字段。问题是您提供了有关该领域的“太多”信息,但该信息内部不一致。

将 Parent 中的字段和注释更改为:

@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST)
@JoinColumn(name = "parent_id")
private List<Child> children;

然后从子实体中完全删除“parentId”。 您之前已指定了 JoinTable 注释。然而,您想要的不是JoinTable。 JoinTable 将创建一个额外的第三个表,以便将两个实体相互关联。你想要的只是一个 JoinColumn 。将 JoinColumn 注释添加到也使用 OneToMany 注释的字段后,您的 JPA 实现将知道您正在将 FK 添加到 CHILD 表中。问题是 JPA 有一个已经定义了列 Parent_id 的 CHILD 表。

想象一下,您为 CHILD 表的函数和 Parent_id 列提供了两个相互冲突的定义。在一种情况下,您已经告诉 JPA 它是一个实体,而parent_id 只是该实体中的一个值。另一方面,您告诉 JPA 您的 CHILD 表不是实体,而是用于在 CHILD 和 PARENT 表之间创建外键关系。问题是您的 CHILD 表已经存在。然后,当您持久化实体时,您已经告诉它parent_id 显式为空(未设置),但您还告诉它您的parent_id 应该更新以设置对父表的外键引用。

我用上面描述的更改修改了您的代码,并且我还称为“坚持”而不是“合并”。

这导致了 3 个 SQL 查询

insert into PARENT (ID) values (default)
insert into CHILD (ID) values (default)
update CHILD set parent_id=? where ID=?

这完美地反映了您想要的。父条目已创建。创建 CHILD 条目,然后更新 CHILD 记录以正确设置外键。

如果您改为添加注释

@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST)
@JoinColumn(name = "parent_id", nullable = false)
private List<Child> children;

然后,当插入子项时,它将运行以下查询

insert into CHILD (ID, parent_id) values (default, ?)

因此从一开始就正确设置你的 FK。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JPA 在 MySQLIntegrityConstraintViolationException 中插入父/子结果 的相关文章

随机推荐