java JPA 在 MySQLIntegrityConstraintViolationException 中插入父/子结果

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/33681921/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 21:59:22  来源:igfitidea点击:

JPA insert parent/child results in MySQLIntegrityConstraintViolationException

javamysqlhibernatejpa

提问by ventsyv

This has already been asked a number of times, but I don't find any good answers so I'll ask it again.

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

I have parent-children unidirectional relation as follows:

我有父子单向关系如下:

@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
}

I create an instance of the parent, assign a list of children to it and try to persist it:

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

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

When the code runs, I get the following exception:

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

MySQLIntegrityConstraintViolationException: Cannot add or update a child row: a foreign key constraint fails (testdb.child, CONSTRAINT parent_fkFOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE NO ACTION ON UPDATE NO ACTION)

MySQLIntegrityConstraintViolationException:无法添加或更新子行:外键约束失败(testdb. child,CONSTRAINT parent_fkFOREIGN KEY(parent_id)REFERENCES parentid)ON DELETE NO ACTION ON UPDATE NO ACTION)

I'm assuming this is due to the fact that the parent is not fully inserted when the child is being attempted to be inserted.

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

If I don't add the children to the parent, the parent gets inserted just fine. I tried switching the GeneratedValue generation strategy but that did not help.

如果我不将孩子添加到父级,则父级会被插入就好了。我尝试切换 GeneratedValue 生成策略,但这没有帮助。

Any ideas how to insert the parent & the children at the same time?

任何想法如何同时插入父母和孩子?

Edit:Even if I persist the parent first, I'm still getting the same error. I determined it's because the parent_id is not set in the child; the child is default constructed and thus the parent_id is set to 0 which does not exist thus the foreign key constraint validation.

编辑:即使我先坚持父母,我仍然遇到同样的错误。我确定这是因为 parent_id 未在 child 中设置;child 是默认构造的,因此 parent_id 设置为 0 不存在,因此外键约束验证。

Is there a way to get jpa to automatically set the parent_id of the children that are assigned to the parent instance?

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

回答by Nathan Kummer

Your relationship does not have to be bi-directional. There is some mis-information in the comments here.

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

You also said that you had added the field "parentId" into the Child entity because you assumed that JPA needs to "know" about the parent field so that it can set the value. The problem is not that JPA does not know about the field, based on the annotations that you have provided. The problem is that you have provided "too much" information about the field, but that information is not internally consistent.

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

Change your field and annotation in Parent to:

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

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

Then remove the "parentId" from the Child entity entirely. You had previously specified a JoinTable annotation. However, what you want is not a JoinTable. A JoinTable would create an additional third table in order to relate the two entities to each other. What you want is only a JoinColumn. Once you add the JoinColumn annotation onto a field that is also annotated with OneToMany, your JPA implementation will know that you are adding a FK into the CHILD table. The problem is that JPA has a CHILD table already defined with a column parent_id.

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

Think of it that you are giving it two conflicting definitions of both the function of the CHILD table and the parent_id column. In one case, you have told you JPA that it is an entity and the parent_id is simply a value in that entity. In the other, you have told JPA that your CHILD table is not an entity, but is used to create a foreign key relationship between your CHILD and PARENT table. The problem is that your CHILD table already exists. Then when you are persisting your entity, you have told it that the parent_id is explicitly null (not set) but then you have also told it that your parent_id should be updated to set a foreign key reference to the parent table.

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

I modified your code with the changes I described above, and I also called "persist" instead of "merge".

我使用上面描述的更改修改了您的代码,并且我还称其为“persist”而不是“merge”。

This resulted in 3 SQL queries

这导致了 3 个 SQL 查询

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

This reflects what you want perfectly. The PARENT entry is created. The CHILD entry is created, and then the CHILD record is updated to correctly set the foreign key.

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

If you instead add the annotation

如果您改为添加注释

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

Then it will run the following query when it inserts the child

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

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

thus setting your FK propertly from the very beginning.

从而从一开始就正确设置您的 FK。

回答by Lorenzo

Adding updatable=false to the parent entity solved the problem with both an insert and an updated being executed on the child table. However, I have no clue why that's the case and in fact, I don't think what I am doing is correct because it means I cannot update the child table later on if I have to.

将 updatable=false 添加到父实体解决了在子表上执行插入和更新的问题。但是,我不知道为什么会这样,事实上,我认为我所做的不正确,因为这意味着如果必须的话,我以后无法更新子表。

回答by Filip

I know persisting a new parent with children works for me using em.persists(...).

我知道坚持一个有孩子的新父母对我有用em.persists(...)

Using em.merge(...), really I don't know, but it sounds like it should work, but obviously you are running into troubles as your JPA implementation is trying to persists children before parent.

使用em.merge(...),真的我不知道,但听起来它应该可以工作,但显然你遇到了麻烦,因为你的 JPA 实现试图在父母之前坚持孩子。

Maybe check if this works for you : https://vnageswararao.wordpress.com/2011/10/06/persist-entities-with-parent-child-relationship-using-jpa/

也许检查这是否适合您:https: //vnageswararao.wordpress.com/2011/10/06/persist-entities-with-parent-child-relationship-using-jpa/

I don't know if this plays a role in your problem, but keep in mind that em.merge(p);will return a managed entity... and pwill remain un-managed, and your children are linked to p.

我不知道这是否在您的问题中起作用,但请记住,这em.merge(p);将返回一个托管实体......并且p将保持不受管理,并且您的孩子与p.

A) try em.persists(...)rather than em.merge(...)

A) 尝试em.persists(...)而不是em.merge(...)

if you can't

如果你不能

B) you are merging parent... and you cascadeis set to CascadeType.PERSIST. Try changing it to

B) 你正在合并parent......并且你cascade被设置为CascadeType.PERSIST. 尝试将其更改为

  • cascade=CascadeType.ALL
    or
  • cascade={CascadeType.PERSIST, CascadeType.MERGE}
  • cascade=CascadeType.ALL
    或者
  • cascade={CascadeType.PERSIST, CascadeType.MERGE}

I know mergewill persists newly created entities and should behave as persists, but these are my best hints.

我知道mergewill 会保留新创建的实体并且应该表现得像persists,但这些是我最好的提示。

回答by Biraj B Choudhury

What you wantto achieve you can achieve with this code.

您可以使用此代码实现您想要实现的目标。

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

        @OneToMany(cascade = CascadeType.PERSIST)
        private List<Child> children;
    }

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

        @ManyToOne
        Parent parent;
    }