[英]How to insert entity with many to many relation?
I'm working with Spring framework.我正在使用 Spring 框架。 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:
以下是我们的课程:
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;
}
}
I have used @ManyToMany annotations to define the relation between them.我使用@ManyToMany 注释来定义它们之间的关系。
At this point, I have in H2 a table Actor that has ID, AGE, NAME and SURNAME columns, table Movie that has ID, GENRE, TITLE and YEAR, and a new table MOVIE_ACTOR because of the annotation with ACTORS_ID and MOVIES_ID columns.此时,我在 H2 中有一个具有 ID、AGE、NAME 和 SURNAME 列的表 Actor,具有 ID、GENRE、TITLE 和 YEAR 的表 Movie,以及一个新表 MOVIE_ACTOR,因为注释带有 ACTORS_ID 和 MOVIES_ID 列。 Until here it seems okey.
直到这里看起来还不错。
Now, if I save a movie (I have implemented the service and repository extending jpaRepository for both entities):现在,如果我保存一部电影(我已经为两个实体实现了扩展 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);
}
(I know that it is not a get request, but just for check if a movie is correctly saved just accessing to the endpoint) What I obtain is that in the table Movie the 4 columns are added correctly, but neither ACTOR and MOVIE_ACTOR are completed so actorList is ommited, this two tables are empty. (我知道这不是一个获取请求,而只是为了检查是否正确保存了仅访问端点的电影)我得到的是在表 Movie 中正确添加了 4 列,但 ACTOR 和 MOVIE_ACTOR 都没有完成所以actorList被省略了,这两个表是空的。 Why this happens and how can I solved it?
为什么会发生这种情况,我该如何解决?
Thank you so much for your help!非常感谢你的帮助!
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})
.您应该将
@ManyToMany
注释更改为@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).这会导致级联合并和持久化操作(保存新的 object 或任何更改都会导致自动更新另一个)。
Also consider adding proper add and remove methods for your lists like described in this article and good equals and hashCode methods .还可以考虑为您的列表添加适当的添加和删除方法,如本文所述,以及良好的equals 和 hashCode 方法。
In general, you could find very good descriptions of Hibernate related issues on the page of Vlad Mihalcea .一般来说,您可以在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);
}
There are 2 issues here.这里有2个问题。
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.其次,在双向关系的情况下,您有责任在内存 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.此处的关系由 Actor(没有
mappedBy
的一侧)管理,但您尚未将任何电影添加到 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.因此,如果您在电影构造函数中迭代演员并添加电影
a.getMovies().add(this)
则双方都将被设置并且数据应按要求保存。
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. Hibernate 文档建议应避免使用
@ManyToMany
映射,因为在大多数情况下,您可能希望存储与关联相关的其他数据:例如,字符名称。 A more flexible option is then to create a Join Entity, say, MovieAppearance which has Movie, Actor, and other properties as required.一个更灵活的选择是创建一个加入实体,例如,MovieAppearance,它具有电影、演员和其他所需的属性。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.