JPA insert parent/child results in MySQLIntegrityConstraintViolationException

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_fk
FOREIGN KEY (parent_id) REFERENCES parent (id) 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.

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?

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

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.

Method 2

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.

Method 3

I know persisting a new parent with children works for me using 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.

Maybe check if this works for you : 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 p will remain un-managed, and your children are linked to p.

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

if you can’t

B) you are merging parent… and you cascade is set to CascadeType.PERSIST. Try changing it to

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

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

Method 4

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


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x