Hibernate 4 的实体关系

原文: https://javabeginnerstutorial.com/hibernate/entity-relations-with-hibernate-4/

上次我介绍了注解而不是 XML 配置。 现在,我将更深入地研究并展示如何创建实体关系并将其映射到数据库。

如果在示例中查看图书实体,您可能会想到:“为什么将图书的作者存储为字符串?” 你是对的。 这使得作者查询书籍几乎是不可能的,或者至少没有表现得那么出色。 而且两次输入之间的错别字会使情况更糟。

在本文中,我将进一步举例说明,然后将作者的字符串提取到另一个实体中。

作者实体

让我们从一个拥有作者信息的新实体开始。 为简单起见,它将仅包含作者的姓名和作者创建的书籍列表。

  1. @Entity
  2. @Table(name = "AUTHORS")
  3. public class Author {
  4. @Id
  5. private String name;
  6. @ManyToMany(mappedBy = "authors")
  7. private final List<Book> books = new ArrayList<>();
  8. private Author() {
  9. }
  10. public Author(String name) {
  11. this.name = name;
  12. }
  13. public String getName() {
  14. return this.name;
  15. }
  16. public void setName(String name) {
  17. this.name = name;
  18. }
  19. public List<Book> getBooks() {
  20. return this.books;
  21. }
  22. @Override
  23. public String toString() {
  24. return MessageFormat.format("{0} has written {1} book(s).", this.name, this.books.size());
  25. }
  26. }

如您所见,这里有一个新的注解:@ManyToMany。 这告诉 Hibernate Author实体映射到其他Book实体。 mappingBy属性用于告诉 Hibernate 哪个实体是关系的所有者。 这主要用于一对多多对一关系中。

将作者添加到Book实体

现在,将作者字符串更改为作者列表。 它看起来与Author实体完全相同:

  1. @Entity
  2. @Table(name = "BOOKS")
  3. public class Book {
  4. @Id
  5. private String isbn;
  6. private String title;
  7. @ManyToMany
  8. private final List<Author> authors = new ArrayList<>();
  9. @Temporal(TemporalType.DATE)
  10. @Column(name = "PUBLISHED_DATE")
  11. private Date published;
  12. private Book() {
  13. }
  14. public Book(String isbn, String title, Date published) {
  15. this.isbn = isbn;
  16. this.title = title;
  17. this.published = published;
  18. }
  19. public String getIsbn() {
  20. return this.isbn;
  21. }
  22. public String getTitle() {
  23. return this.title;
  24. }
  25. public void setTitle(String title) {
  26. this.title = title;
  27. }
  28. public List<Author> getAuthors() {
  29. return this.authors;
  30. }
  31. public void setIsbn(String isbn) {
  32. this.isbn = isbn;
  33. }
  34. public Date getPublished() {
  35. return this.published;
  36. }
  37. public void setPublished(Date published) {
  38. this.published = published;
  39. }
  40. @Override
  41. public String toString() {
  42. final String authorNames = this.authors.stream().map(Author::getName).collect(Collectors.joining(", "));
  43. return MessageFormat.format("{0} by {1} (ISBN: {2}), published {3}", this.title, authorNames, this.isbn, this.published);
  44. }
  45. }

在这里,我没有在@ManyToMany注解中添加任何参数,因为作者是所有者。

关系类型

当然,多对多关系不是唯一可用的关系。 如果需要,您也可以选择一对一一对多多对一

一对一关系

对于一个实体,此关系类型仅包含一种引用类型,而对于另一实体也是如此。 在这种情况下,您可以选择用于存储引用的实体。例如,如果我将 ISBN 提取为具有其他一些属性的单独实体,则可能是书籍与 ISBN 号之间的关系:一本书具有一本 ISBN 和一本 ISBN 准确地指一本书。

一对多和多对一关系

在这种情况下,一个实体在其他实体中具有许多引用。 如果一本书只能有一位作者,那就是这种情况。 在这种情况下,引用 I​​D 将存储在BOOKS表中,因为有一本书参考了其作者。 我们在作者实体中使用一对多,在Book实体中使用多对一

多对多关系

如您在第一个示例中所看到的,这种关系有点复杂。 在这里,我们需要一个单独的表格来包含书籍和作者之间的引用。 这就是所谓的联接表。 该表由 Hibernate 自动维护,但是您可以告诉该表如何命名。 如果您不选择名称,则 Hibernate 会使用实体名称,并用下划线将其分开,所有者名称为站点实体。 在此示例中,联接表名为:BOOKS_AUTHORS

更改main方法

由于实体已更改,因此我也必须更改Main类的main方法。

  1. final Book book = new Book("9781617291999", "Java 8 in Action", new Date());
  2. session.beginTransaction();
  3. Arrays.stream("Raoul-Gabriel Urma,Mario Fusco,Alan Mycroft".split(",")).map(name -> new Author(name)).forEach(author -> {
  4. author.getBooks().add(book);
  5. book.getAuthors().add(author);
  6. session.save(author);
  7. });
  8. session.save(book);
  9. session.getTransaction().commit();

如您所见,我使用一些 lambda 来动态创建作者。 有趣的部分是最后一个语句,forEach块:我将这本书添加到当前作者的图书列表中,然后将当前作者添加到这本书的作者列表中。 以后需要在数据库中一起查找书籍和作者时,需要使用此引用(如果您手动查询或使用 Hibernate 加载数据集)。

现在的结果与上次有所不同:

  1. ----
  2. Storing 1 books in the database
  3. Java 8 in Action by Raoul-Gabriel Urma, Mario Fusco, Alan Mycroft (ISBN: 9781617291999), published 2015.06.29\. 16:57
  4. ----

如果我放弃声明book.getAuthors().add(author);,则结果不包含作者的姓名:

  1. ----
  2. Storing 1 books in the database
  3. Java 8 in Action by (ISBN: 9781617291999), published 2015.06.29\. 16:58
  4. ----

总结

如您所见,有很多选项可以将实体关系映射到规范化数据库。 下次,我将向您展示如何将实体继承映射到数据库。

代码下载

您可以从 Github 此处下载特定章节的代码。