本文介绍了JPA在MySQLIntegrityConstraintViolationException中插入父/子结果的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



我有父母子女单向关系如下:

pre $
@Table(name =PARENT)
public class父$ {b $ b @Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name =ID)
private长parentId;

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

$ b $实体
@Table(name =CHILD)
public class Child {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name =ID)
private长ID;

@Column(name =PARENT_ID)
private long parentId;

//其他字段
}

我创建一个父母的实例,为其分配一个孩子的列表,并尝试持久化它:

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

运行代码时,出现以下异常:

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



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



任何想法如何插入父&孩子在同一时间?



编辑:即使我先坚持父母,我仍然得到相同的错误。我确定这是因为parent_id没有设置在孩子身上;这个孩子是默认构造的,因此parent_id被设置为0,因此不存在外键约束验证。



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

解决方案

您的关系不一定是双向的。在这里的评论中有一些误解。



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



将您的字段和注释更改为父级:

  @OneToMany(fetch = FetchType.EAGER,cascade = CascadeType.PERSIST)
@JoinColumn(name =parent_id)
私人列表< Child>儿童;

然后完全从Child实体中删除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应该被更新来设置一个到父表的外键引用。



我修改了上述代码,并且我也称之为persist而不是merge。

这导致了3个SQL查询插入到父(ID)值(默认值)中
插入到CHILD(ID)值(默认)
更新CHILD设置parent_id =?其中ID =?

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



如果您添加注释

  @OneToMany(fetch = FetchType.EAGER,cascade = CascadeType.PERSIST)
@JoinColumn(name =parent_id,nullable = false)
私人列表< Child>儿童;

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

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

从一开始就设置你的FK。


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:

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.

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.

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

解决方案

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.

Change your field and annotation in Parent to:

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

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.

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

This resulted in 3 SQL queries

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.

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.

这篇关于JPA在MySQLIntegrityConstraintViolationException中插入父/子结果的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

11-02 19:20