Spring Data/JPA: How do I save an item with multiple child collections?

半城伤御伤魂 提交于 2021-02-11 15:04:36

问题


I am struggling with understanding how to use Spring Data/JPA to interact with a database. I'm using the simple H2 database currently for testing purposes.

I have a main item, Task, that in addition to fields, also has two collections of "child" data: TaskNote and StateChangeHistory.

In my task service class, I have a @PostConstruct method to set up some dummy data and save it to the database so that I can retrieve and display it later. It appears that the Task is being saved, but none of the children are. When I load the data into a view (or inspect the data in the debugger), the child collections are always empty.

What do I need to do to ensure that the Task and it's child items are all saved?

Here is some of the code:

Task.java:

@Entity
@NamedEntityGraph(name = "graph.task", attributeNodes = { @NamedAttributeNode("notes") })
public class Task extends AbstractEntity {
  @OneToMany(mappedBy = "task")
  private List<StateChangeHistory> stateHistory = new ArrayList<>();

  @OneToMany(mappedBy = "task")
  private List<TaskNote>           notes        = new ArrayList<>();
  ...
}

TaskNote.java:

@Entity
public class TaskNote extends AbstractEntity {
  @ManyToOne
  private Task   task;
  ...
}

TaskRepository.java:

public interface TaskRepository extends JpaRepository<Task, Long> {
  @Override
  @EntityGraph(attributePaths = { "notes" })
  public List<Task> findAll();
}

TaskService.java:

@Service
public class TaskService {
  private TaskRepository      taskRepository;

  public TaskService(TaskRepository taskRepository) {
    this.taskRepository = taskRepository;
  }
  ...
  @PostConstruct
  public void populateTestData() {
    if (taskRepository.count() == 0) {
      Task task = null;
      TaskNote note = null;

      // Task 1
      task = new Task("Task-1");
      task.setPriority(TaskPriority.HIGH);
      task.setType(TaskType.RECURRING);

      // Note 1
      note = new TaskNote();
      note.setNote("note-1");
      task.getNotes().add(note);

      // Note 2
      note = new TaskNote();
      note.setNote("note-2");
      task.getNotes().add(note);

      taskRepository.save(task);
      
      // Task 2
      taskRepository.save(new Task("Task-2"));
    }
  }
}

As you can see, I create a task object and add two notes to it and then try to save it. I assume the save of the task works, as I can get them to display in my view. However, the notes list is always empty.

What do I need to do to save the child data along with the parent?


回答1:


As stated in other answers you have to use @OneToMany(cascade=CascadeType.ALL) over Task notes.

To make code work you have to set a two-way entity relationship:

Task task = new Task("Task-1");

TaskNote note = new TaskNote();
note.setNote("note-1");
note.setTask(task);   // missing in your code
task.getNotes().add(note);

taskRepository.save(task);



回答2:


There are three ways how to save the status of an entity using JPA.

  1. Load the entity from the database and make changes to its state. JPA keeps track of these changes and will flush them (i.e. perform DML-statements) before the end of the transaction, typically exactly at the end of the transaction.

  2. Create entities and save them to a repository

  3. Perform #2 with a different entity which references your entity with the reference annotated with cascade=CascadeType.ALL. Actually MERGE and PERSIST are sufficient but I think if you cascade you should cascade all.




回答3:


Solution

You can solve it by just adding cascade= CascadeType.ALL in @OneToMany annotation as a property .



来源:https://stackoverflow.com/questions/65081753/spring-data-jpa-how-do-i-save-an-item-with-multiple-child-collections

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!