hibernate EntityManager -连接表中的ManyToMany子实体持久性

uz75evzq  于 2023-08-06  发布在  其他
关注(0)|答案(1)|浏览(118)

我有一个Book实体,它有两个子实体AuthorGenre,每个子实体都有一个ManyToMany关系和相应的连接表。Book类定义如下:

@Entity
@Getter
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Book {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "book_seq")
    @GenericGenerator(name = "book_seq",
            strategy = "com.example.bookappjpa.dao.StringPrefixedSequenceIdGenerator",
            parameters = {
                    @org.hibernate.annotations.Parameter(name = StringPrefixedSequenceIdGenerator.INCREMENT_PARAM, value = "1"),
                    @org.hibernate.annotations.Parameter(name = StringPrefixedSequenceIdGenerator.VALUE_PREFIX_PARAMETER, value = "B"),
                    @org.hibernate.annotations.Parameter(name = StringPrefixedSequenceIdGenerator.NUMBER_FORMAT_PARAMETER, value = "%08d"),
            })
    private String id;

    @NaturalId
    private String title;

    @Column(name = "rank_no")
    private int rank;

    private int listId;

    private Double rating;

    private int pages;

    private LocalDate publishedOn;

    @ManyToMany()
    @JoinTable(name = "BOOK_AUTHOR", joinColumns = @JoinColumn(name = "BOOK_ID"), inverseJoinColumns = @JoinColumn(name = "AUTHOR_ID")
    )
    private Set<Author> authors = new HashSet<>();

    @ManyToMany
    @JoinTable(name = "BOOK_GENRE", joinColumns = @JoinColumn(name = "BOOK_ID"), inverseJoinColumns = @JoinColumn(name = "GENRE_ID"))
    private Set<Genre> genres = new HashSet<>();

    public void setTitle(String title) {
        this.title = title;
    }

    public void setRankNo(int rankNo) {
        this.rank = rankNo;
    }

    public void setListId(int listId) {
        this.listId = listId;
    }

    public void setRating(Double rating) {
        this.rating = rating;
    }

    public void setPages(int pages) {
        this.pages = pages;
    }

    public void setPublishedOn(LocalDate publishedOn) {
        this.publishedOn = publishedOn;
    }

    public boolean addAuthor(Author author) {
        return this.authors.add(author);
    }

    public boolean addGenre(Genre genre) {
        return this.genres.add(genre);
    }

}

字符串
持久化方法(通过其自然id查找每个实体,以查看它是否已经存在于DB中,如果不存在,则仅继续持久化。请原谅半成品代码,有待改进):

@Transactional
    public void addBook(BookDTO book) {
        Session session = entityManager.unwrap(Session.class);
        Book b = session.bySimpleNaturalId(Book.class).load(book.getTitle());

        if (null == b) {
            Book bookToDB = new Book();
            bookToDB.setTitle(book.getTitle());
            bookToDB.setPages(book.getPages());
            bookToDB.setRating(book.getRating());
            bookToDB.setRankNo(book.getRank());
            bookToDB.setPublishedOn(book.getPublishedOn());

            book.getAuthors().forEach(a -> {
                Author authorToDB = new Author(a.getName(), a.getAge(), a.getNationality());
                if(null == session.bySimpleNaturalId(Author.class).load(a.getName())) {
                    authorToDB.addBook(bookToDB);
                    entityManager.persist(authorToDB);
                } 
            });

            book.getGenres().forEach(g -> {
                if(null == session.bySimpleNaturalId(Genre.class).load(g.getName())) {
                    Genre genreToDB = new Genre(g.getName());
                    genreToDB.addBook(bookToDB);
                    entityManager.persist(genreToDB);
                }
            });

            entityManager.persist(bookToDB);

        } else {
            String message = "Book with title " + book.getTitle() + " already exists!";
            log.info(message);
            throw new RuntimeException(message);
        }

        session.close();
    }


这种方法的问题是,例如,如果某本书使用特定的AuthorGenre持久化(s),稍后,我们有另一个Book,具有相同的AuthorGenre(s),将按照上面的代码跳过子实体的持久化,并且在连接表中不填充任何内容,表示父Book和子Author/Genre(s)之间没有关系。我真的应该为AuthorGenre实体包括上面的检查吗?如果没有,我将在相应的实体表中得到多个实体,它们具有相同的AuthorGenre名称,尽管具有不同的ID序列号(主键)。我不确定这是不是一个好的练习。想知道是否有一种复杂的/effii方法来处理生产级代码中的这种情况。提前感谢,如果这是一个新手的问题,我道歉
两个Book对象具有相同的作者(第二个没有进入连接表BOOK_AUTHOR,因为作者已经存在于DB中,并且我的DAO方法在这种情况下不允许再次持久化):

{
    "rank": 8,
    "title": "Clap When You Land",
    "authors": [
      {
        "name": "Elizabeth Acevedo",
        "age": 0,
        "nationality": null
      }
    ],
    "rating": 0,
    "genres": [
      {

        "name": "Young Adult"
      },
      {

        "name": "Poetry"
      },
      {

        "name": "Contemporary"
      },
      {

        "name": "Fiction"
      },
      {

        "name": "Audiobook"
      },
      {

        "name": "LGBT"
      },
      {

        "name": "Realistic Fiction"
      }
    ],
    "pages": 432,
    "publishedOn": "2020-05-05",
    "listId": 1
  }

{
    "rank": 18,
    "title": "The Poet X",
    "authors": [
      {

        "name": "Elizabeth Acevedo",
        "age": 0,
        "nationality": null
      }
    ],
    "rating": 0,
    "genres": [
      {

        "name": "Poetry"
      },
      {

        "name": "Young Adult"
      },
      {

        "name": "Contemporary"
      },
      {

        "name": "Fiction"
      },
      {

        "name": "Audiobook"
      },
      {

        "name": "Realistic Fiction"
      },
      {

        "name": "Romance"
      }
    ],
    "pages": 368,
    "publishedOn": "2018-03-06",
    "listId": 1
  }

yh2wf1be

yh2wf1be1#

要保持关系的一致性,并确保将现有的体裁和作者添加到新书中:
通过自然ID获取流派和作者,并执行以下操作

  • 如果它们存在,请将它们添加到帐簿
  • 如果它们不存在,请创建它们并将其添加到Book
book.getAuthors().forEach(a -> {
     Author author = session.bySimpleNaturalId(Author.class).load(a.getName());
     if (author == null) {
         author = new Author(a.getName(), a.getAge(), a.getNationality());
         em.persist(author);
     }
     bookToDB.addAuthor(author);
 });

 book.getGenres().forEach(g -> {
     Genre genre = session.bySimpleNaturalId(Genre.class).load(g.getName());
     if (genre == null) {
         genre = new Genre(g.getName());
         em.persist(genre);
     }
     bookToDB.addGenre(genre);
 });

字符串
您的Genre和Author实体应该包括以下与书籍的双向关系

@Entity
public class Genre {
...
   @ManyToMany(mappedBy = "genres")
   private Set<Book> books = new HashSet<>();
...
}

@Entity
public class Author {
...
   @ManyToMany(mappedBy = "authors")
   private Set<Book> books = new HashSet<>();
...
}


并在Book实体中展开addAuthor(...)和addGenre(...)方法,以确保在添加它们时双向关系是同步的

@Entity
public class Book {
...
    public boolean addAuthor(Author author) {
        author.getBooks().add(this); // ensures both side of relationship
        return this.authors.add(author);
    }

    public boolean addGenre(Genre genre) {
        genre.getBooks().add(this); // ensures both side of relationship
        return this.genres.add(genre);
    }
...
}

相关问题