Hibernate 4 的实体关系
原文: https://javabeginnerstutorial.com/hibernate/entity-relations-with-hibernate-4/
上次我介绍了注解而不是 XML 配置。 现在,我将更深入地研究并展示如何创建实体关系并将其映射到数据库。
如果在示例中查看图书实体,您可能会想到:“为什么将图书的作者存储为字符串?” 你是对的。 这使得作者查询书籍几乎是不可能的,或者至少没有表现得那么出色。 而且两次输入之间的错别字会使情况更糟。
在本文中,我将进一步举例说明,然后将作者的字符串提取到另一个实体中。
作者实体
让我们从一个拥有作者信息的新实体开始。 为简单起见,它将仅包含作者的姓名和作者创建的书籍列表。
@Entity
@Table(name = "AUTHORS")
public class Author {
@Id
private String name;
@ManyToMany(mappedBy = "authors")
private final List<Book> books = new ArrayList<>();
private Author() {
}
public Author(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public List<Book> getBooks() {
return this.books;
}
@Override
public String toString() {
return MessageFormat.format("{0} has written {1} book(s).", this.name, this.books.size());
}
}
如您所见,这里有一个新的注解:@ManyToMany
。 这告诉 Hibernate Author
实体映射到其他Book
实体。 mappingBy
属性用于告诉 Hibernate 哪个实体是关系的所有者。 这主要用于一对多或多对一关系中。
将作者添加到Book实体
现在,将作者字符串更改为作者列表。 它看起来与Author
实体完全相同:
@Entity
@Table(name = "BOOKS")
public class Book {
@Id
private String isbn;
private String title;
@ManyToMany
private final List<Author> authors = new ArrayList<>();
@Temporal(TemporalType.DATE)
@Column(name = "PUBLISHED_DATE")
private Date published;
private Book() {
}
public Book(String isbn, String title, Date published) {
this.isbn = isbn;
this.title = title;
this.published = published;
}
public String getIsbn() {
return this.isbn;
}
public String getTitle() {
return this.title;
}
public void setTitle(String title) {
this.title = title;
}
public List<Author> getAuthors() {
return this.authors;
}
public void setIsbn(String isbn) {
this.isbn = isbn;
}
public Date getPublished() {
return this.published;
}
public void setPublished(Date published) {
this.published = published;
}
@Override
public String toString() {
final String authorNames = this.authors.stream().map(Author::getName).collect(Collectors.joining(", "));
return MessageFormat.format("{0} by {1} (ISBN: {2}), published {3}", this.title, authorNames, this.isbn, this.published);
}
}
在这里,我没有在@ManyToMany
注解中添加任何参数,因为作者是所有者。
关系类型
当然,多对多关系不是唯一可用的关系。 如果需要,您也可以选择一对一,一对多或多对一。
一对一关系
对于一个实体,此关系类型仅包含一种引用类型,而对于另一实体也是如此。 在这种情况下,您可以选择用于存储引用的实体。例如,如果我将 ISBN 提取为具有其他一些属性的单独实体,则可能是书籍与 ISBN 号之间的关系:一本书具有一本 ISBN 和一本 ISBN 准确地指一本书。
一对多和多对一关系
在这种情况下,一个实体在其他实体中具有许多引用。 如果一本书只能有一位作者,那就是这种情况。 在这种情况下,引用 ID 将存储在BOOKS
表中,因为有一本书参考了其作者。 我们在作者实体中使用一对多,在Book
实体中使用多对一。
多对多关系
如您在第一个示例中所看到的,这种关系有点复杂。 在这里,我们需要一个单独的表格来包含书籍和作者之间的引用。 这就是所谓的联接表。 该表由 Hibernate 自动维护,但是您可以告诉该表如何命名。 如果您不选择名称,则 Hibernate 会使用实体名称,并用下划线将其分开,所有者名称为站点实体。 在此示例中,联接表名为:BOOKS_AUTHORS
。
更改main方法
由于实体已更改,因此我也必须更改Main
类的main
方法。
final Book book = new Book("9781617291999", "Java 8 in Action", new Date());
session.beginTransaction();
Arrays.stream("Raoul-Gabriel Urma,Mario Fusco,Alan Mycroft".split(",")).map(name -> new Author(name)).forEach(author -> {
author.getBooks().add(book);
book.getAuthors().add(author);
session.save(author);
});
session.save(book);
session.getTransaction().commit();
如您所见,我使用一些 lambda 来动态创建作者。 有趣的部分是最后一个语句,forEach
块:我将这本书添加到当前作者的图书列表中,然后将当前作者添加到这本书的作者列表中。 以后需要在数据库中一起查找书籍和作者时,需要使用此引用(如果您手动查询或使用 Hibernate 加载数据集)。
现在的结果与上次有所不同:
----
Storing 1 books in the database
Java 8 in Action by Raoul-Gabriel Urma, Mario Fusco, Alan Mycroft (ISBN: 9781617291999), published 2015.06.29\. 16:57
----
如果我放弃声明book.getAuthors().add(author);
,则结果不包含作者的姓名:
----
Storing 1 books in the database
Java 8 in Action by (ISBN: 9781617291999), published 2015.06.29\. 16:58
----
总结
如您所见,有很多选项可以将实体关系映射到规范化数据库。 下次,我将向您展示如何将实体继承映射到数据库。
代码下载
您可以从 Github 此处下载特定章节的代码。