0

Suppose I've One-To-Many Relationship: One-To-Many

import javax.persistence.*;
import java.io.Serializable;
import java.util.Set;

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

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String title;
    private String author;
    @Column(unique = true)
    private String isbn;

    @OneToMany(mappedBy = "book", fetch = FetchType.LAZY,
            cascade = CascadeType.ALL)
    private Set<Page> pages;

    public Book() {
    }

    public Book(String title, String author, String isbn) {
        this.title = title;
        this.author = author;
        this.isbn = isbn;
    }

    // getters and setters, equals(), toString() .... (omitted for brevity)
}

And

import javax.persistence.*;
import java.io.Serializable;

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

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private int number;
    private String content;
    private String chapter;

    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "book_id", nullable = false)
    private Book book;

    public Page() {
    }

    public Page(int number, String content, String chapter, Book book) {
        this.number = number;
        this.content = content;
        this.chapter = chapter;
        this.book = book;
    }

    // getters and setters, equals(), toString() .... (omitted for brevity)
}

And store it with:

// create a new book
Book book = new Book("Java 101", "John Doe", "123456");
    
// save the book
bookRepository.save(book);
    
// create and save new pages
pageRepository.save(new Page(1, "Introduction contents", "Introduction", book));
pageRepository.save(new Page(65, "Java 8 contents", "Java 8", book));
pageRepository.save(new Page(95, "Concurrency contents", "Concurrency", book));

The question is: do I really need to save every page manually? Can I just work with java collections and then just save the main entity like:

book.pages.add(new Page(1, "Introduction contents", "Introduction", book));

book.pages.add(new Page(65, "Java 8 contents", "Java 8", book));

book.pages.add(new Page(95, "Concurrency contents", "Concurrency", book));

bookRepository.save(book);

The example was taken from https://attacomsian.com/blog/spring-data-jpa-one-to-many-mapping

And I wanna know the right and the best way to store relationships, that's why I'm asking here and not just experiment by myself, because it seems the article misleading tons of people

MWiesner
  • 8,868
  • 11
  • 36
  • 70
J.J. Beam
  • 2,612
  • 2
  • 26
  • 55
  • "*... it seems the article misleading tons of people*" - Why do you think that? – Turing85 Dec 26 '21 at 11:06
  • 1
    Does this answer your question? [JPA - Persisting a One to Many relationship](https://stackoverflow.com/questions/1795649/jpa-persisting-a-one-to-many-relationship) – Turing85 Dec 26 '21 at 11:11
  • So if add `@OneToMany(cascade = {CascadeType.PERSIST})` then pages will be stored after `bookRepository.save(book)` right? – J.J. Beam Dec 26 '21 at 11:21
  • Why not try it out? – Turing85 Dec 26 '21 at 11:21
  • `Why not try it out?` - great question, I specially answered on it beforehand at the tail of my question. I saw a guy here, who `try everything out` and after his experiments exposed mysql db is not comply ACID rules LOL, sorry I need a professional answer not my experiments. It seems I gotta add it on every my question – J.J. Beam Dec 26 '21 at 11:24
  • You asked a very specific question ("*So if add `@OneToMany(cascade = {CascadeType.PERSIST})` then pages will be stored after `bookRepository.save(book)` right?*"). That is something you can try out. – Turing85 Dec 26 '21 at 11:25
  • I don't think it's a specific question, sorry. The article misleads people, tons of people will produce code when store all dependencies manually instead of just `@OneToMany(cascade = {CascadeType.PERSIST})` (if I got right your reference) – J.J. Beam Dec 26 '21 at 11:28
  • 1
    Since you already have a `... cascade = CascadeType.ALL ...` on the one-side and set the reference to the one-side correctly on the many-side, the 2nd code block provided should work as expected. – Turing85 Dec 26 '21 at 11:31

1 Answers1

1

Here is what cascade on the Book#pages comes into play. As you configured it as CascadeType.ALL, it means that when you use EntityManager to persist() on a Book, it will automatically call persist() on all its Page.

With cascade disabled, you have to call the following to save a new Book and all of its pages:

entityManager.persist(book);
entityManager.persist(page1);
entityManager.persist(page2);
....
entityManager.persist(pageN);

But with cascade enabled, you just have to call the following as all the persist() on Page will be cascaded to be called automatically.

entityManager.persist(book);

As the spring-data-jpa repositories internally just use the same EntityManager to save objects for the same transaction, you just need to call the following to save the book and its pages if cascade is enabled:

Book book = new Book("Java 101", "John Doe", "123456");
book.pages.add(new Page(1, "Introduction contents", "Introduction", book));
book.pages.add(new Page(65, "Java 8 contents", "Java 8", book));
book.pages.add(new Page(95, "Concurrency contents", "Concurrency", book));
bookRepository.save(book);

Otherwise if cascade is disabled, you have to call the following to save a Book and its pages:

Book book = new Book("Java 101", "John Doe", "123456");
bookRepository.save(book);

pageRepository.save(new Page(1, "Introduction contents", "Introduction", book));
pageRepository.save(new Page(65, "Java 8 contents", "Java 8", book));
pageRepository.save(new Page(95, "Concurrency contents", "Concurrency", book));
Alexey Veleshko
  • 792
  • 1
  • 18
Ken Chan
  • 84,777
  • 26
  • 143
  • 172