JPA - Data not getting saved in mapped entity while using many to many relationship

reiley :

I'm working on a JPA application, where I've a many-to-many relationship between 2 entities, Squad & Product.

Squad.java

import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;

@Entity(name = "SQUAD")
public class Squad extends AuditModel {

    private long id;
    private String name;
    private Set<Product> products = new HashSet<>();

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    @Column(name = "NAME")
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return super.toString();
    }

    @ManyToMany(fetch = FetchType.LAZY,
            cascade = {
                    CascadeType.PERSIST,
                    CascadeType.MERGE
            })
    @JoinTable(name = "SQUAD_PRODUCT",
            joinColumns = {@JoinColumn(name = "SQUAD_ID")},
            inverseJoinColumns = {@JoinColumn(name = "PRODUCT_ID")})
    public Set<Product> getProducts() {
        return products;
    }

    public void setProducts(Set<Product> products) {
        this.products = products;
    }
}

Product.java

import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;

@Entity(name = "PRODUCT")
public class Product extends AuditModel {

    private long id;
    private String name;
    private Set<Squad> squads = new HashSet<>();

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    @Column(name = "NAME")
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @ManyToMany(fetch = FetchType.LAZY,
            cascade = {
                    CascadeType.PERSIST,
                    CascadeType.MERGE
            },
            mappedBy = "products")
    public Set<Squad> getSquads() {
        return squads;
    }

    public void setSquads(Set<Squad> squads) {
        this.squads = squads;
    }
}

I'm using @RepositoryRestResource(for the first time) to generate basic rest controllers:

SquadRepository.java

@RepositoryRestResource(collectionResourceRel = "squad", path = "squad")
public interface SquadRepository  extends CrudRepository<Squad, Long> {

    List<Squad> findByName(@Param("name") String name);

}

ProductRepository.java

@RepositoryRestResource(collectionResourceRel = "product", path = "product")
public interface ProductRepository extends CrudRepository<Product, Long> {

    List<Product> findByName(@Param("name") String name);

}

Now I want to store data in Squad using a REST call. I also want to store a Product within this Squad, so I'm using below Rest call:

http://localhost:8080/squad

{
    "name": "MyFirstSquad",
    "product": 
        {
            "name": "MyFirstProduct"
        }

}

Response I get is:

{
  "createDateTime": "2019-12-15T17:53:37.234",
  "updateDateTime": "2019-12-15T17:53:37.234",
  "name": "MyFirstSquad",
  "_links": {
    "self": {
      "href": "http://localhost:8080/squad/1"
    },
    "squad": {
      "href": "http://localhost:8080/squad/1"
    },
    "products": {
      "href": "http://localhost:8080/squad/1/products"
    }
  }
}

But when I open http://localhost:8080/squad/1/products, it empty:

{
  "_embedded" : {
    "product" : [ ]
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/squad/1/products"
    }
  }
}

Any suggestion, what am I doing wrong? Is my rest call incorrect, or, something missing in my code?

Thanks

dimirsen :

Have you inspected your DB in a viewer? When you use ManyToMany relation, an intermediate table gets created - SQUAD_PRODUCT. It contains pairs of numbers. That means, before you can add a number/id of a product, it has to be present in the table PRODUCTS. So, firstly, you create a product via POST request. Secondly, use its generated id for adding into set. I guess, it can be done this way:

1st step, POST
{"name": "MyFirstProduct"}

2nd step POST to create squad
{"name": "MyFirstSquad"}

3rd step PATCH created squad (alter it) - request PATCH http://localhost:8080/squad/1 - assuming it was created with id 1
{"name": "MyFirstSquad", "product":["http://localhost:8080/products/1"]} - assuming the product was created with id 1. 

You can list several products here. Please notice, that it will rewrite the list in DB.

I don't have any means to check these JSONs so you may need some amendments.

Personally, I would check the same saving in java code in case there are issues in repos.

I recommend FireFox plugin RESTClient.

May be unrelated, but I don't like these lines:

 private Set<Squad> squads = new HashSet<>();
 private Set<Product> products = new HashSet<>();

they may drop the collections. But I can be too cautious :)

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=358878&siteId=1