如何插入具有多对多关系的实体?

时间:2019-09-26 14:44:53

标签: java spring spring-boot spring-data-jpa

我正在使用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被省略,这两个表为空。为什么会发生这种情况,我该如何解决?

非常感谢您的帮助!

2 个答案:

答案 0 :(得分:1)

也许您在服务方法中实现了类似的操作(未显示),但是我认为它丢失了:您没有cascade任何东西(分别保存了另一个类的对象)。您应将@ManyToMany批注更改为@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})。这导致级联合并和持久操作(保存一个新对象或进行任何更改会导致自动更新另一个对象)。

还要考虑为列表添加适当的添加和删除方法,如本article和好的equals and hashCode methods中所述。

通常,您可以在Vlad Mihalcea页面上找到有关Hibernate相关问题的很好的说明。

更新:基于@Alan Hay发表的实现

型号

@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和其他属性。