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:
{
"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
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 :)