簡體   English   中英

Hibernate - JPA - OneToMany 和 CascadeType.ALL

[英]Hibernate - JPA - OneToMany and CascadeType.ALL

我有一個關於@OneToMany 的 JPA 問題,特別是 CascadeType.ALL 的功能。 我有兩個實體,一個父親和一個孩子。

我向您展示了我所擁有的示例:BOOK

@Entity
@Table(name = "books")
public class Book implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Integer id;

    @OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY)
    @JoinColumn(name="id_page")
    private List<Page> pages;
    ...
    ...
}

@Entity
@Table(name = "pages")
public class Page implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "id")
    private Integer id;

    @Column(name = "id_book")
    private int idBook;
    ...
    ...
}

所以,我只有一本書和幾頁。 由於 CascadeType.ALL,當我保存這本書時,我也會保存其中包含的頁面。 哪個有效,實際上 Hibernate 試圖保存 Book 對象,通過 OneToMany 意識到還有 Page 實體要保存並啟動查詢,這要歸功於 Cascade ALL。

我只是這樣做:

bookRepository.save(book);

代表我想保存的書的 json 是這樣的:

{
  "id": 0,
  "page": [
    {
      "id": 0,
      "idBook": 0
    }
  ]
}

但是,如果我將 0 放入圖書 ID,則會創建它。 如果我將 idBook 上的 0 放入頁面,則會出現如下錯誤:表“book”中不存在 Key (id_book) = (0)。

但是,我真正不明白的是:為什么如果我將一個 id_book 放在數據庫上存在的 json 中,然后完成保存書實體,它會理解 id 是什么並將其正確插入到數據庫中?

所以,如果我給這個 JSON.

{
  "id": 0,
  "page": [
    {
      "id": 0,
      "idBook": 1
    }
  ]
}

讓我們考慮一下,在 DB 上只有一本 id=1 的書。 保存后,它會為我生成一行 id=2 的書,以及一個鏈接到 id=2 的書的頁面?

這件事讓我發瘋。 如果給一個 idBook = 0 告訴他他必須檢索/生成它,他會告訴我它不存在。 另一方面,如果我給他一本真正存在的idBook(但它不正確),他甚至不考慮並正確使用他剛剛創建的Book的idBook嗎?

我只是不想使用偽造的 idBook,告訴他他必須在創建書后獲取頁面 idBook,就像他實際所做的那樣。

根據Page實體中id_book列的存在,我得出結論,您希望使用雙向映射。 嘗試這樣做,以孩子作為關系的所有者:

@Entity
@Table(name = "pages")
public class Page implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "id")
    private Integer id;

    @ManyToOne(fetch = FetchType.LAZY) // EAGER by default
    @JoinColumn(name = "id_book")
    private Book book;
    ...
    ...
}
@Entity
@Table(name = "books")
public class Book implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Integer id;

    @OneToMany(mappedBy = "book", cascade = CascadeType.ALL) // Lazy fetch type by default
    private List<Page> pages = new ArrayList<>();
    ...
    ...
    
    // obligatory synchronization methods
    public void addPage(Page page) {
        pages.add(page);
        page.setBook(this);
    }

    public void removePage(Page page) {
        pages.remove(this);
        page.setBook(null);
    }
}

考慮到“我通過一本書,一個頁面(或者可能是多個頁面)”,我也想分享我的解決方案。

與其他答案有點相似,我也在使用雙向。 我也包括集成測試。

圖書實體

@Entity
public class Book {

    @Id
    @GeneratedValue(generator = "uuid")
    @GenericGenerator(name = "uuid", strategy = "uuid2")
    private String id;

    @OneToMany(mappedBy = "book", cascade = { CascadeType.PERSIST, CascadeType.MERGE })
    private List<Page> pages;

    public Book(String id, List<Page> pages) {
        this.id = id;
        this.pages = pages;
        this.pages.stream().forEach(page -> page.attachedTo(this));
    }

    public String getId() {
        return id;
    }

    public List<Page> getPages() {
        return pages;
    }

    // Required by JPA
    protected Book() {
    }

}

頁面實體

@Entity
public class Page {

    @Id
    @GeneratedValue(generator = "uuid")
    @GenericGenerator(name = "uuid", strategy = "uuid2")
    private String id;

    private String content;

    @ManyToOne
    private Book book;

    public Page(String content) {
        this.content = content;
    }

    void attachedTo(Book book) {
        this.book = book;
    }

    public String getId() {
        return id;
    }

    public Book getBook() {
        return book;
    }

    // Required by JPA
    protected Page() {}
}

集成測試

@Test
@Transactional
public void saveBookWithPages() {
    List<Page> firstBookPages = new ArrayList<>();
    firstBookPages.add(new Page("content-0001"));
    firstBookPages.add(new Page("content-0002"));
    firstBookPages.add(new Page("content-0003"));

    Book firstBook = new Book("first-book", firstBookPages);
    firstBook = bookRepository.save(firstBook);

    List<Page> secondBookPages = new ArrayList<>();
    secondBookPages.add(new Page("content-0004"));
    secondBookPages.add(new Page("content-0005"));

    Book secondBook = new Book("second-book", secondBookPages);
    secondBook = bookRepository.save(secondBook);

    // query the first and second book
    firstBook = bookRepository.findById(firstBook.getId()).orElseThrow(() -> new IllegalArgumentException("book id not found."));
    secondBook = bookRepository.findById(secondBook.getId()).orElseThrow(() -> new IllegalArgumentException("book id not found"));

    Assert.assertEquals(3, firstBook.getPages().size());  // check if first book has 3 pages
    Assert.assertEquals(2, secondBook.getPages().size()); // check if second book has 2 pages

    // query the first page of second book
    Page secondBookFirstPage = pageRepository.findById(secondBook.getPages().get(0).getId()).orElseThrow(() -> new IllegalArgumentException(""));

    Assert.assertEquals(secondBook.getId(), secondBookFirstPage.getBook().getId());  // check if the page is correctly associated to the book
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM