Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring data rest POSTing a new item with ManyToMany relationship

I have two entities: Actor and Movie. Between these two exists a ManyToMany relationship (because and Actor can join more than one Movie and in a Movie you can see more than one Actor). On my Spring Data Rest API I have the following endpoints:

http://host:port/movies
http://host:port/actors

Now suppose I would create a new actor from the movie page. My client will submit a (single) POST request with the actor information and the relationship with the movie. I tried with something like the following (a new actor for the movie with id 1):

{ 
  "name": "Leonardo Di Caprio",
  "movies": [ "http://host:port/movies/1" ]
}

Spring API replies with a 201 Created, so the format and the movie URI are fine. When I query API or DB for the actor, I discover that the actor has been created but the relationship does not exists.

I already know that you should make two requests ( one to create the actor and one to create the relationship ) for ManyToMany relationships with Spring data rest. I'm asking here if there is a way to create both with a single request (like for OneToMany/ManyToOne or OneToOne relationships.

Actor class

@Entity
public class Actor {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    private String name;

    @ManyToMany(mappedBy = "actors")
    private List<Movie> movies;

    public long getId() {
        return id;
    }

    public String getName() {
        return name;
    }

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

    public List<Movie> getMovies() {
        return movies;
    }

    public void setMovies(List<Movie> movies) {
        this.movies = movies;
    }
}

Movie Class

@Entity
public class Movie {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    protected long id;

    protected String title;

    @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH})
    protected List<Actor> actors;

    public long getId() {
        return id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public List<Actor> getActors() {
        return actors;
    }

    public void setActors(List<Actor> actors) {
        this.actors = actors;
    }

}

For both entities, I have standard repositories:

@Repository
public interface ActorRepository extends PagingAndSortingRepository<Actor, Long> {
}

UPDATE

The behaviour that I was facing was due to how JPA handles ManyToMany relationships. In this thread there is a clever explanation on how to handle bidirectional associations with JPA and REST.

I can solve my problem with one of these two options:

A - Doing two POST requests, one on

http://host:port/actors 

to persist the new Actor and one on the

http://host:port/movies/{id}/actors 

as the following:

...                                              |
Content-Type: text/uri-list                      | headers
...                                              |

http://host:port/actors/{id-of-the-new-actor}    | body

to persist the association between the new actor and the movie.

B - Doing only one POST request on

http://host:port/actors 

(as I described at the beginning of the question) but modifying the setMovies method in the Actor class (as described in the thread I cited).

like image 903
Francesco Avatar asked Aug 20 '16 18:08

Francesco


1 Answers

First create the resources : create the actor resource :

curl -i -X POST -H "Content-Type:application/json"
    -d "{\"name\":\"Leonardo Di Caprio\"}" http://host:port/actors

then create the movies :

curl -i -X POST -H "Content-Type:application/json"
  -d "{\"title\":\"Titanic\"}" http://host:port/movies

finaly create the association (supposing http://host:port/actors/1 is dicaprio uri):

curl -i -X PUT -H "Content-Type:text/uri-list"
  --data-binary @movies.txt http://host:port/actors/1/movies

with movies.txt containing the movie's uris, each on a separate line:

http://host:por/movies/1
http://host:por/movies/2

follow this useful link

like image 192
naydnabil Avatar answered Oct 16 '22 14:10

naydnabil