Spring Data / JPA: как мне сохранить элемент с несколькими дочерними коллекциями?

#java #spring-boot #spring-data-jpa

#java #spring-boot #spring-data-jpa

Вопрос:

Я изо всех сил пытаюсь понять, как использовать Spring Data / JPA для взаимодействия с базой данных. В настоящее время я использую простую базу данных H2 для целей тестирования.

У меня есть основной элемент, Task , который в дополнение к полям также содержит две коллекции «дочерних» данных: TaskNote и StateChangeHistory .

В моем классе обслуживания задач у меня есть @PostConstruct метод настройки некоторых фиктивных данных и сохранения их в базе данных, чтобы я мог получить и отобразить их позже. Похоже, что Task сохраняется, но ни один из дочерних элементов не сохраняется. Когда я загружаю данные в представление (или проверяю данные в отладчике), дочерние коллекции всегда пусты.

Что мне нужно сделать, чтобы убедиться, что задача и все ее дочерние элементы сохранены?

Вот часть кода:

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

Как вы можете видеть, я создаю объект task и добавляю к нему две заметки, а затем пытаюсь сохранить его. Я предполагаю, что сохранение задачи работает, поскольку я могу заставить их отображаться в моем представлении. Однако список заметок всегда пуст.

Что мне нужно сделать, чтобы сохранить дочерние данные вместе с родительскими?

Комментарии:

1. Решит ли это проблему, если вы добавите атрибут ‘cascade’ в аннотацию @OneToMany? каскад=CascadeType.ALL

Ответ №1:

Как указано в других ответах, вы должны использовать @OneToMany(cascade=CascadeType.ALL) over Task notes .

Чтобы заставить код работать, вы должны установить двустороннюю связь сущностей:

 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:

Существует три способа сохранения статуса объекта с помощью JPA.

  1. Загрузите объект из базы данных и внесите изменения в его состояние. JPA отслеживает эти изменения и Удаляет их (т. Е. Выполняет DML-инструкции) до завершения транзакции, обычно точно в конце транзакции.
  2. Создайте объекты и save их в репозиторий
  3. Выполните # 2 с другим объектом, который ссылается на ваш объект со ссылкой cascade=CascadeType.ALL , аннотированной с. На самом деле MERGE и PERSIST достаточно, но я думаю, что если вы каскадируете, вы должны каскадировать все.

Ответ №3:

Решение

Вы можете решить эту проблему, просто добавив cascade= CascadeType.ALL аннотацию @OneToMany в качестве свойства.