Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Data REST + JPA remove from OneToMany collection [not owner side]

Currently we have an issue (a well known one) with Spring Data JPA + Spring Data REST (Hibernate as JPA implementation) when trying to update the collection (relation) which is a not the owning side.

The mapping is the following:

@Entity(name = Product.NAME)
public class Product {
...
@OneToMany(mappedBy = "baseProduct", fetch = FetchType.LAZY, targetEntity = Variant.class)
List<Variant> getVariants() {
... 

and on the other variant side:

@Entity(name = Variant.NAME)
public class Variant extends Product {
...
@ManyToOne(fetch = FetchType.LAZY, targetEntity = Product.class)
@JoinColumn(name = "baseproduct_id", referencedColumnName = "id")
Product getBaseProduct() {
...
}

all is good on the Java side if you use Spring Data JPA only, however if you want to update the "product" by updating its collection of variants and send PATCH request to https://localhost:8112/storefront/rest/product/21394435410197232 containing the payload of the new collection only (having 2 out of the 3 items):

{"variants":["22801810293768080","22801810293768096"]}

I get no exceptions or anything but since the owning side is the other side nothing is persisted and I got the old 3 items again.

I know that I can fix this by setting

@JoinColumn(name = "baseproduct_id", referencedColumnName = "id")

on both sides and not use mappedBy anywhere, however I have heard there is a performance implication which I am not sure how big it is (we got 100+ entities having @OneToMany) and I wonder is there better workaround via @PreUpdate listener or something ?

like image 850
JOKe Avatar asked Jan 20 '17 11:01

JOKe


1 Answers

You have to synchronize both sides of the bidirectional association, and also add on orphanRemoval and Cascade.

So, your mapping becomes:

@OneToMany(
    mappedBy = "baseProduct", 
    fetch = FetchType.LAZY, 
    targetEntity = Variant.class
    cascade = CascadeType.ALL, 
    orphanRemoval = true)
List<Variant> getVariants() {

And the two add/remove methods:

public void addVariant(Variant variant) {
    getVariants().add(variant);
    variant.setBaseProuct(this);
}

public void removeVariant(Variant variant) {
    variant.setBaseProuct(null);
    this.getVariants().remove(variant);
}

You need to implement equals and hashCode methods in the Variant child entity for the add and remove methods to work effectively.

like image 82
Vlad Mihalcea Avatar answered Oct 14 '22 18:10

Vlad Mihalcea