当前位置: 首页 > 面试题库 >

JPA在MySQLIntegrityConstraintViolationException中插入父/子结果

阎俊英
2023-03-14
问题内容

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

我有如下的父子单向关系:

@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:不能添加或更新子行,外键约束失败(testdbchild,约束parent_fk
外键(parent_id)参考parentid)ON DELETE NO ACTION ON UPDATE NO ACTION)

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

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

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

编辑:
即使我先坚持父母,我仍然遇到相同的错误。我确定是因为未在子级中设置parent_id;默认情况下构造子对象,因此将parent_id设置为0(该值不存在),从而进行外键约束验证。

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


问题答案:

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

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

在“父项”中将字段和注释更改为:

@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的CHILD表已经定义了parent_id列。

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

我通过上述更改修改了您的代码,并且我也称“持久”而不是“合并”。

这导致了3个SQL查询

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

这充分反映了您想要的东西。PARENT条目已创建。创建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。



 类似资料:
  • 子外键引用为空。这个外键只是父表的主键。 我下面有两个表,其中有@OneToMany(父)和@ManyToOne(子)映射。记录插入到父项以及子项中,子项中的外键值除外。 下面的确认中有什么遗漏吗? 我在github中上传了代码:https://github.com/harshalpatil2012/Springboot

  • 我有一个场景,需要在一种情况下加载所有子值,在另一种情况下加载一些特定的子值。我对这两种情况都使用一个bean,并使用命名查询编写查询。 现在在我的查询2中,我只需要加载字符串A,而不需要加载字符串B和字符串C。我试过使用 但得到以下错误 那么,关于如何继续这方面的任何建议。。

  • 使用JPA(Hibernate)我试图实现以下关系,并想知道其他人是否对最佳方法有任何建议: 基本上是完全不相关的对象,每个对象都有一个公共子对象的集合;在对象模型中实现简单,在数据库中稍微麻烦些! 我确信这一定是常见的事情,但是我很难找到任何示例实现,因为我真的不知道正确的搜索词。 谢谢你的时间!

  • 我的父母子女单向关系如下: 我创建一个父实例,给它分配一个子列表,并尝试持久化它,它工作得很好。 当我试图通过子实体单独保存时,我看到insert查询试图将null插入子表中的列PARENT_ID,并导致 异常,同时保存独立于父表的子实体。我试图插入的子实体与已经存在的父实体相关。 在将关系定义为单向关系后,是否无法直接保存子实体?

  • 这是我的数据库 我生成了 NetBeans 实体类,它给出了以下代码;**** 我是JPA的新手,我只想通过输入“products_id”来获得“订单号”,这是可能的吗?** 我想要这样的东西**