Spring Data Jpa OneToMany сохранить дочерние и родительские объекты одновременно? - PullRequest
0 голосов
/ 02 февраля 2020

Это моя родительская сущность. Примечание: для краткости удаляем геттеры, сеттеры, аннотации ломбков.

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

    private String title;

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

Ниже представлена ​​моя дочерняя сущность

@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;
}

Каждый Board может иметь несколько Story, но каждый Story принадлежит одному Board.

Сейчас где-то в моей службе я делаю это:

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());
}

Один из подходов, который я могу предпринять, - это сначала сохранить доску без Set<Story> и затем сохраните истории с этой сохраненной доской, установленной как ссылка. Но для этого потребуются два вызова репозитория и код, который не будет хорошо выглядеть.

Кроме того, причина, по которой у меня возникают проблемы, заключается в том, что до тех пор, пока я не выполню этот код, моя БД пуста. То есть это новая запись, которую мы вводим впервые. Так что у Board table еще нет рядов.

Так есть ли еще способ сделать это за один выстрел? Для большинства других вопросов о стековом потоке сущность платы уже выбрана из db, и затем добавляют к ней дочерние сущности и сохраняют ее в db. Но для меня база данных полностью свободна sh, и я хочу добавить первую новую родительскую сущность и соответствующие дочерние сущности одновременно, по крайней мере, в коде, даже если hibernate делает несколько вызовов базы данных.

1 Ответ

4 голосов
/ 02 февраля 2020

Да, вам просто нужно каскадно передать изменения от родителя к потомку:

@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<>();
}

Теперь, когда вы сохраняете родителя (Board), изменения будут каскадно добавляться к дочерней таблице. Вы также можете использовать CascadeType.ALL вместо {CascadeType.PERSIST, CascadeType.MERGE} для каскадирования любых изменений, например удаления (когда вы удаляете дочерний элемент из коллекции родительского объекта, объединяющий идентификатор в дочерней таблице будет удален).

...