I\'m working with Spring framework. I have two entities, Movie and Actor, so a Movie can have many actors and an Actor can play in many Movie. Following we have the classes:
There are 2 issues here.
Firstly, you have not set the cascade options on the relationship.
@ManyToMany(mappedBy = "movies", cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private List<Actor> actors;
Secondly, in the case of a bi-directional relationship it is your responsibility to maintain both sides of the relationship in the in-memory model. The relationship here is being managed by Actor (the side without mappedBy
) but you have not added any movies to the movies collection in Actor.
So, if you iterate actors in your movies constructor and add the movie a.getMovies().add(this)
then both sides will be set and the data should be saved as requested.
The Hibernate docs suggest @ManyToMany
mappings should be avoided as in most cases you are likely to want to store additional data related to the association: in your case for example, character name. A more flexible option is then to create a Join Entity, say, MovieAppearance which has Movie, Actor, and other properties as required.
Maybe you implemented something like this in your service methods (you did not show it) but I would assume that it is missing: You do not cascade anything (respectively save objects of the other class). You should change your @ManyToMany
annotation to @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
. This leads to cascading the merge and persist operation (saving a new object or any changes leads to automatically updating the other one).
Also consider adding proper add and remove methods for your lists like described in this article and good equals and hashCode methods.
In general, you could find very good descriptions of Hibernate related issues on the page of Vlad Mihalcea.
@Entity
public class Actor {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String surname;
private String age;
@ManyToMany
@JoinTable(name = "movie_actor")
private List<Movie> movies = new ArrayList<>();
public void addMovie(Movie movie) {
movies.add(movie);
movie.getActors().add(this);
}
public void removeMovie(Movie movie) {
movies.remove(movie);
movie.getActors().remove(this);
}
// Constructors, getters and setters...
// Equals and hashCode methods a la
// https://vladmihalcea.com/how-to-implement-equals-and-hashcode-using-the-jpa-entity-identifier/
}
@Entity
public class Movie {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private String genre;
private String year;
@ManyToMany(mappedBy = "movies", cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private List<Actor> actors;
public Movie(String title, String genre, String year, List<Actor> actors) {
this.title = title;
this.genre = genre;
this.year = year;
actors.forEach(a -> a.addMovie(this));
}
// Getters and setters...
}
@GetMapping("/create")
public void create() {
Actor actor1 = new Actor("Pedro", "Perez", "40");
Actor actor2 = new Actor("Alfredo", "Mora", "25");
Actor actor3 = new Actor("Juan", "Martinez", "20");
Actor actor4 = new Actor("Mario", "Arenas", "30");
List<Actor> actorList = new ArrayList<>();
actorList.add(actor1);
actorList.add(actor2);
actorList.add(actor3);
actorList.add(actor4);
Movie movie = new Movie("Titanic", "Drama", "1984", actorList);
movieService.create(movie);
}