我正在使用Spring Boot starter spa,hibernate和Postgres来完成本教程,试图找出如何使用多对多关系跨越关系插入数据。我遵循了本教程:many-to-many tutorial但似乎在运行示例后,发布者表格为发布者a和发布者b提供了重复的发布者条目。谁能解释如何阻止这种情况发生?我已经读到了实施。实体的hashcode和equals方法可以解决这个问题,但是在为实体植入以下代码之后,我仍然会遇到同样的问题。
@Entity
public class Publisher {
private int id;
private String name;
private Set<Book> books;
public Publisher(){
}
public Publisher(String name){
this.name = name;
}
public Publisher(String name, Set<Book> books){
this.name = name;
this.books = books;
}
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@ManyToMany(mappedBy = "publishers")
public Set<Book> getBooks() {
return books;
}
public void setBooks(Set<Book> books) {
this.books = books;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Publisher publisher = (Publisher) o;
return name.equals(publisher.name);
}
@Override
public int hashCode() {
return name.hashCode();
}
}
和
@Entity
public class Book {
private int id;
private String name;
private Set<Publisher> publishers;
public Book() {
}
public Book(String name) {
this.name = name;
}
public Book(String name, Set<Publisher> publishers){
this.name = name;
this.publishers = publishers;
}
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name = "book_publisher",
joinColumns = @JoinColumn(name = "book_id",
referencedColumnName = "id"),
inverseJoinColumns = @JoinColumn(name = "publisher_id", referencedColumnName = "id"))
public Set<Publisher> getPublishers() {
return publishers;
}
public void setPublishers(Set<Publisher> publishers) {
this.publishers = publishers;
}
@Override
public String toString() {
String result = String.format(
"Book [id=%d, name='%s']%n",
id, name);
if (publishers != null) {
for(Publisher publisher : publishers) {
result += String.format(
"Publisher[id=%d, name='%s']%n",
publisher.getId(), publisher.getName());
}
}
return result;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Book book = (Book) o;
return name.equals(book.name);
}
@Override
public int hashCode() {
return name.hashCode();
}
}
select * from publisher
[
{"id": 2,"name": "Publisher B"},
{"id": 3,"name": "Publisher A"},
{"id": 5,"name": "Publisher C"},
{"id": 6,"name": "Publisher B"},
{"id": 7,"name": "Publisher A"}
]
为什么id = 6且id = 7?
答案 0 :(得分:2)
要回答您的问题,我们必须仔细查看触发对数据库的所有查询的public void run(String... strings)
方法 - https://github.com/hellokoding/jpa-manytomany-springboot-hsql/blob/master/src/main/java/com/hellokoding/jpa/HelloJpaApplication.java
第35和49行之间有:
final Publisher publisherA = new Publisher("Publisher A");
final Publisher publisherB = new Publisher("Publisher B");
final Publisher publisherC = new Publisher("Publisher C");
bookRepository.save(new HashSet<Book>(){{
add(new Book("Book A", new HashSet<Publisher>(){{
add(publisherA);
add(publisherB);
}}));
add(new Book("Book B", new HashSet<Publisher>(){{
add(publisherA);
add(publisherC);
}}));
}});
此部分负责保留前3个发布者:发布者A,发布者B和发布者C.首先Book
,&#34;预订A&#34;创建发布者A和发布者B.并且因为这些发布者被分配给一个值,所以Hibernate将这些实体标记为&#34; persisted&#34;。感谢这一点&#34; Book B&#34;正在保存,只创建了Publisher C和&#34; Book B&#34;使用对发布者A的现有引用,并且不创建新对象。
现在让我们来看看第56和70行之间会发生什么:
// save a couple of publishers
final Book bookA = new Book("Book A");
final Book bookB = new Book("Book B");
publisherRepository.save(new HashSet<Publisher>() {{
add(new Publisher("Publisher A", new HashSet<Book>() {{
add(bookA);
add(bookB);
}}));
add(new Publisher("Publisher B", new HashSet<Book>() {{
add(bookA);
add(bookB);
}}));
}});
在这里,我们可以看到相反的情况。此处Book
对象与相关Publisher
一起保存,而不是保存Publisher
并将Book
与每本书相关联。关键信息是通过publisherRepository.save()
调用保存的两个发布者都是全新的对象 - 这两个对象都是使用Set.add()
方法中的构造函数实例化的。这两个发布者是完全新的对象,它们只与Publisher A和Publisher B共享相同的名称。但是从对象标识的角度来看,它们不是同一个对象。这段代码还将创建两个Book
对象。只有两个,因为两个发布者共享对同一本书A和书B对象的引用。
这就是为什么当你列出所有发布者时,你会得到这样的东西:
Publisher A -|
Publisher C |----- created in bookRepository.save() call
Publisher B -|
Publisher A -|----- created in publisherRepository.save() call
Publisher B -|
我希望它有所帮助。