【问题标题】:How to insert entity with many to many relation?如何插入具有多对多关系的实体?
【发布时间】:2020-01-26 21:12:19
【问题描述】:

我正在使用 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);
    }

(我知道这不是获取请求,而只是为了检查是否正确保存了仅访问端点的电影)我得到的是在表 Movie 中正确添加了 4 列,但 ACTOR 和MOVIE_ACTOR 已完成,因此省略了 actorList,这两个表是空的。为什么会发生这种情况,我该如何解决?

非常感谢您的帮助!

【问题讨论】:

  • 嗨,这不是建立多对多关系的最佳方式,请查看此帖子 vladmihalcea.com/… 以获得最佳和最有效的方式。

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


【解决方案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);
}

【讨论】:

  • 我添加了@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}),现在演员被添加到演员表中。但是还有一个问题,由movie id和actor id组成的关系表是空的,没有完成。有什么想法吗?
  • 是的!我忘记编辑评论了,抱歉。现在它正确插入到所有表 Movies、Actors 和关系表中。现在的问题是,如果我在数据库中插入一个已经在其他电影中的演员的电影,它不能正常工作,它应该添加新电影,然后在关系表中创建关系,但它不应该添加演员再次在数据库中。如何解决并引用数据库中已经存在的参与者?
  • 您必须从数据库中检索要使用的 Actor 实体。然后,应该引用这个。不再需要创建新的 Actor。
【解决方案2】:

这里有两个问题。

首先,您没有在关系上设置级联选项。

@ManyToMany(mappedBy = "movies", cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private List<Actor> actors;

其次,在双向关系的情况下,您有责任在内存模型中维护关系的双方。这里的关系是由 Actor 管理的(没有mappedBy 的一方),但是您还没有将任何电影添加到 Actor 中的电影集合中。

因此,如果您在电影构造函数中迭代演员并添加电影a.getMovies().add(this),那么双方都将被设置并且数据应按要求保存。

Hibernate 文档建议应避免使用 @ManyToMany 映射,因为在大多数情况下,您可能希望存储与关联相关的其他数据:例如,字符名称。一个更灵活的选择是创建一个 Join Entity,比如 MovieAppearance,它具有 Movie、Actor 和其他所需的属性。

【讨论】:

    猜你喜欢
    • 2011-07-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-21
    • 2014-09-16
    • 2023-03-05
    • 2015-08-25
    相关资源
    最近更新 更多