自动从父级中删除子级

问题描述 投票:0回答:1
@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)

简短说明:

  1. 保存作者
  2. 保存一本书并将该书添加到作者的书籍中
  3. 将作者加载到新事务中,以确保该书与作者正确关联
  4. 直接将书取出
  5. 加载作者以确保该书自动从作者的书中删除

第 5 步不正确。

如您所见,这本书并未从其父目录中删除。作者至今还保留着这本书的参考资料。据我的理解,这本书应该自动从作者实体的集合中删除?!

java hibernate jpa hibernate-mapping
1个回答
0
投票

发生这种情况是因为

author2
在移除书籍之前加载。
author2
的收藏中仍然有这本书,并且已经处于持久化上下文中。一旦事务结束,账本就会再次刷新到数据库。如果我删除
find
author2
方法,一切都会正常工作。

正如 Vlad Mihalcea here 提到的,最佳实践是始终同步实体关系的双方,无论是

@OneToMany
@OneToOne
还是
@ManyToMany

他是这样解释的:

但是,我们仍然需要双方同步,否则,我们会破坏“域”>“模型”关系的一致性,并且无法保证实体状态转换 除非双方正确同步才能工作。

如果双向关联不同步并且只有子级可以 引用父级,而父级不包含子级 集合,那么您就有可能将您的模型暴露在棘手的错误中 根据存在或不存在做出错误的决定 父母的孩子集合中的孩子。

并且,在 Hibernate 上下文中,只有同步双向关联是 保证正确地保存在数据库中。即使您在特定的 Hibernate 版本上观察到即使关联不同步它也能正常工作,但也不能保证在您升级到较新版本的 Hibernate 时它也能正常工作。

出于这些原因,Post实体定义了addComment和removeComment实体状态同步方法。

© www.soinside.com 2019 - 2024. All rights reserved.