Spring Data Jpa OneToMany 同时保存子实体和父实体?

the*_*mer 9 java spring set spring-data spring-boot

这是我的父实体。注意:为简洁起见,删除 getter、setter、lombok 注释。

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

    private String title;

    @OneToMany(mappedBy = "board")
    private Set<Story> stories = new HashSet<>();
}
Run Code Online (Sandbox Code Playgroud)

下面是我的子实体

@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;
}
Run Code Online (Sandbox Code Playgroud)

每个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());
}
Run Code Online (Sandbox Code Playgroud)

我可以采取的一种方法是先保存电路板Set<Story>,然后将这个保存的电路板设置为 ref 保存故事。但这需要两个 Repository 调用和代码明智的它看起来不太好。

另外,我遇​​到问题的原因是因为在运行此代码之前,我的数据库是空的。这是我们第一次进入的新记录。所以Board table还没有行。

那么有没有办法一次性做到这一点?对于 stackoverflow 上的大多数其他问题,板实体已经从 db 中获取,然后他们正在向其中添加子实体并将其保存到 db。但对我来说,db 是全新的,我想同时添加第一个新的父实体及其相应的子实体,即使 hibernate 进行多个 db 调用,至少在代码方面也是如此。

And*_*cus 5

是的,您只需要将更改从父级级联到子级:

@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<>();
}
Run Code Online (Sandbox Code Playgroud)

现在,无论何时保存父表 ( Board),更改都会级联到子表。您还可以使用CascadeType.ALL代替{CascadeType.PERSIST, CascadeType.MERGE}级联任何更改,例如删除(当您从父实体的集合中删除子项时,子表中的连接 ID 将被删除)。

  • @ragavramachandran OP使用了此处描述的解决方案并且有效:https://vladmihalcea.com/the-best-way-to-map-a-onetomany-association-with-jpa-and-hibernate/ (2认同)