我正在使用Spring框架。我有两个实体,电影和演员,因此电影可以有许多演员,而演员可以在许多电影中播放。以下是我们的课程:
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
@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;
public Actor(String name, String surname, String age) {
this.name = name;
this.surname = surname;
this.age = age;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public List<Movie> getMovies() {
return movies;
}
public void setMovies(List<Movie> movies) {
this.movies = movies;
}
}
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
@Entity
public class Movie {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private String genre;
private String year;
@ManyToMany(mappedBy = "movies")
private List<Actor> actors;
public Movie(String title, String genre, String year, List<Actor> actors) {
this.title = title;
this.genre = genre;
this.year = year;
this.actors = actors;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getGenre() {
return genre;
}
public void setGenre(String genre) {
this.genre = genre;
}
public String getYear() {
return year;
}
public void setYear(String year) {
this.year = year;
}
public List<Actor> getActors() {
return actors;
}
public void setActors(List<Actor> actors) {
this.actors = actors;
}
}
我已经使用@ManyToMany批注定义它们之间的关系。
这时,我在H2中有一个具有ID,AGE,NAME和SURNAME列的表Actor,具有ID,GENRE,TITLE和YEAR的表Movie和一个新表MOVIE_ACTOR,这是因为带有ACTORS_ID和MOVIES_ID的注释列。到这里为止似乎还不错。
现在,如果我保存电影(我已经实现了扩展两个实体的jpaRepository的服务和存储库):
@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);
}
(我知道这不是一个get请求,只是为了检查影片是否正确保存,只是访问端点)。我得到的是在Movie表中正确添加了4列,但是ACTOR和MOVIE_ACTOR已完成,因此actorList被省略,这两个表为空。为什么会发生这种情况,我该如何解决?
非常感谢您的帮助!
答案 0 :(得分:1)
也许您在服务方法中实现了类似的操作(未显示),但是我认为它丢失了:您没有cascade任何东西(分别保存了另一个类的对象)。您应将@ManyToMany
批注更改为@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
。这导致级联合并和持久操作(保存一个新对象或进行任何更改会导致自动更新另一个对象)。
还要考虑为列表添加适当的添加和删除方法,如本article和好的equals and hashCode methods中所述。
通常,您可以在Vlad Mihalcea页面上找到有关Hibernate相关问题的很好的说明。
@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);
}
答案 1 :(得分:1)
这里有2个问题。
首先,您尚未在关系上设置级联选项。
@ManyToMany(mappedBy = "movies", cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private List<Actor> actors;
第二,在双向关系的情况下,您有责任在内存模型中维护关系的双方。这里的关系是由Actor(没有mappedBy
的一面)管理的,但是您尚未在Actor的电影收藏中添加任何电影。
因此,如果您在电影构造函数中迭代演员并添加电影a.getMovies().add(this)
,则将设置双方,并且应根据请求保存数据。
Hibernate文档建议避免使用@ManyToMany
映射,因为在大多数情况下,您可能希望存储与关联相关的其他数据:例如,字符名称。然后,更灵活的选择是创建一个联接实体,例如MovieAppearance,它具有所需的Movie,Actor和其他属性。