Spring Data Jpa OneToMany save child and parent entities at the same time?

2022-09-04 22:50:01

This is my parent entity. Note: Removing getters, setters, lombok annotations for brevity.

@Entity
public class Board {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String title;

    @OneToMany(mappedBy = "board")
    private Set<Story> stories = new HashSet<>();
}

Below is my child entity

@Entity
public class Story {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String title;

    @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "board_id")
    @JsonIgnore
    private Board board;
}

Each can have multiple , but each belongs to single .BoardStoryStoryBoard

Now somewhere in my service, I am doing this:

public void addBoard(BoardDTO boardDto){
    // create a new board object which is just a pojo
    // by copying properties from boardDto
    Board board = ...;

    // create set of stories
    List<String> defaultTitles = Arrays.asList("Todo", "In-Progress", "Testing", "Done");
    Set<Story> stories = defaultTitles.stream().map(title -> Story.builder()
            .title(title)
            // assign a reference, I know this is wrong since board here is not
            // saved yet or fetched from db, hence the question
            .board(board) 
            .build())
            .collect(Collectors.toSet());

    // This saves board perfectly, but in Story db, the foreign key column
    // board_id is null, rightfully so since call to story table was not yet done.
    Board save = boardRepository.save(Board.builder()
            .title(board.getTitle())
            .stories(stories)
            .build());
}

One approach that I can take is to save the board first without and then do a save on stories with this saved board set as ref. But this would need two Repository calls and code wise it wouldn't look good. Set<Story>

Also, the reason why I am having trouble is because until I run this code, my db is empty. That is this is a new record that we are entering for the first time. So has no rows yet.Board table

So is there anyway to do this in a single shot? For most of the other questions on stackoverflow, the board entity is already fetched from db and then they are adding child entities to it and saving it to db. But for me, the db is completely fresh and I want to add a first new parent entity and its corresponding child entities at the same time, at least code wise even if hibernate makes multiple db calls.


答案 1

Yes, you simply need to cascade the changes from the parent to the child:

@Entity
public class Board {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String title;

    @OneToMany(mappedBy = "board", cascade = {CascadeType.PERSIST, CascadeType.MERGE})
    private Set<Story> stories = new HashSet<>();
}

Now whenever you save the parent (), the changes will cascade to the child table. You can also use instead of to cascade any changes, like removal (when you remove the child from the collection on the parent entity, the joining id in the child table will be removed).BoardCascadeType.ALL{CascadeType.PERSIST, CascadeType.MERGE}


答案 2

推荐