简体   繁体   中英

Spring JPA merge parent entity from new child entity

I've searched the SO and didn't find an appropriate solution for this. Say I have a parent entity:

@Entity
public class Parent {
    @Id
    @GeneratedValue
    private int id;

    @OneToMany(mappedBy = "parent", cascade = CascadeType.REMOVE)
    private List<Child> childList;
}

And a child entity, which have foreign key associated with the parent entity:

@Entity
public class Child {
    @Id
    @GeneratedValue
    private int id;

    @JoinColumn(name = "parentId")
    @ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
    private Parent parent;

}

My scenario is a little bit special. The child entity is generated in a huge quantity every hour and I would need to save them into the database. Those child objects may have same parents, and the associate parent entity may already exists in the database. My requirement is to save all these child objects without query out the managed parent entity from entityManager, and if the parent entity exists, just merge/update the existed one. Such as:

Child c = new Child();
// set some properties of child
Parent p = new Parent();
// set some properties from my data into the parent. The parent may already exists
child.setParent(p);
JpaRepo.saveAndFlush(child);// If parent already exists, merge with the existed parent object, otherwise, create new parent object.

Apparently this doesn't work. When parent entity doesn't exist, it will correctly create the parent entity and associated with it. But if the parent already exists, it will throw exception about duplicate key, and if I set the Id of the parent (use dummy value to force make it go through merge), it will throw exception of detached entity object.

Due to the performance constraint, I can't load the parent entity from database as there's too many of them. So is there any way to automatically for JPA or database to merge the object when primary key violate?

I'm using MySQL, is that possible to use ON DUPLICATE KEY UPDATE?

It is a bit tricky staff you would like to achieve. If lazy loading etc is not an option (which I prefer) I recommend to You create an another entity:

@Entity
@Table(name = "sameAsTheOriginal")
public class ChildSaveObject {
    @Id
    @GeneratedValue
    private int id; //I think int will run out fast if there are a lot of objects but it is your choice. (Int max value: 2 147 483 647 is not that mutch) I prefer Long az id

    @Column(name = "parentId")
    private int parent; //note that You need to have the id of the parent object

    //Other properties...
}

In this flow you have to check if the parent object exists and use the ChildSaveObject to save your additional child to it.

I do not really like one to many mappings in most of the cases, I think they are just roots of a lot of problems. Still if you prefer this way, my recommendation is to use fetch = FetchType.LAZY and get the parent entity (this way it will not load all the childs you do not need).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM