@Entity
public class Book implements Serializable {
@Id
private Long bookId;
private String title;
@ManyToOne
@JoinColumn(name = "author_id")
private Author author;
// getter and setter removed for brevity
}
@Entity
public class Author implements Serializable {
@Id
@Column(name = "author_id")
private Long authorId;
private String name;
@OneToMany(mappedBy = "author", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
private Set<Book> books = new HashSet<>();
// getter and setter removed for brevity
public void addBook(Book book) {
getBooks().add(book);
book.setAuthor(this);
}
}
考虑以下测试
@Test
public void manyToOneAndRemove() {
System.out.println("Test");
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
Author author = new Author();
author.setAuthorId(1L);
author.setName("Deepak Kumar");
em.merge(author);
em.flush();
Book book = new Book();
book.setBookId(11L);
book.setTitle("Welcome to CSS");
em.merge(book);
em.flush();
Author author1 = em.find(Author.class, 1L);
author1.addBook(book);
em.merge(author1);
em.getTransaction().commit();
em.close();
em = emf.createEntityManager();
em.getTransaction().begin();
Author author2 = em.find(Author.class, 1L);
System.out.println("Load 1:");
author2.getBooks().forEach(b -> {
System.out.printf("Book: (%s,%s)", b.getBookId(), b.getTitle());
System.out.println();
});
Book aBook = em.find(Book.class, 11L);
em.remove(aBook);
em.flush();
Author author3 = em.find(Author.class, 1L);
System.out.println("Load 2:");
author3.getBooks().forEach(b -> {
System.out.printf("Book: (%s,%s)", b.getBookId(), b.getTitle());
System.out.println();
});
em.getTransaction().commit();
em.close();
em = emf.createEntityManager();
em.getTransaction().begin();
Author author4 = em.find(Author.class, 1L);
System.out.println("Load 3:");
author4.getBooks().forEach(b -> {
System.out.printf("Book: (%s,%s)", b.getBookId(), b.getTitle());
System.out.println();
});
em.getTransaction().commit();
em.close();
}
输出将是
Test
Load 1:
Book: (11,Welcome to CSS)
Load 2:
Book: (11,Welcome to CSS)
Load 3:
Book: (11,Welcome to CSS)
简短说明:
第 5 步不正确。
如您所见,这本书并未从其父目录中删除。作者至今还保留着这本书的参考资料。据我的理解,这本书应该自动从作者实体的集合中删除?!
发生这种情况是因为
author2
在移除书籍之前加载。 author2
的收藏中仍然有这本书,并且已经处于持久化上下文中。一旦事务结束,账本就会再次刷新到数据库。如果我删除 find
的 author2
方法,一切都会正常工作。
正如 Vlad Mihalcea here 提到的,最佳实践是始终同步实体关系的双方,无论是
@OneToMany
、@OneToOne
还是 @ManyToMany
。
他是这样解释的:
但是,我们仍然需要双方同步,否则,我们会破坏“域”>“模型”关系的一致性,并且无法保证实体状态转换 除非双方正确同步才能工作。
如果双向关联不同步并且只有子级可以 引用父级,而父级不包含子级 集合,那么您就有可能将您的模型暴露在棘手的错误中 根据存在或不存在做出错误的决定 父母的孩子集合中的孩子。
并且,在 Hibernate 上下文中,只有同步双向关联是 保证正确地保存在数据库中。即使您在特定的 Hibernate 版本上观察到即使关联不同步它也能正常工作,但也不能保证在您升级到较新版本的 Hibernate 时它也能正常工作。
出于这些原因,Post实体定义了addComment和removeComment实体状态同步方法。