【问题标题】:Spring Data/JPA: How do I save an item with multiple child collections?Spring Data/JPA:如何保存具有多个子集合的项目?
【发布时间】:2020-11-30 21:58:26
【问题描述】:

我正在努力理解如何使用 Spring Data/JPA 与数据库进行交互。我目前正在使用简单的 H2 数据库进行测试。

我有一个主要项目Task,除了字段之外,还有两个“子”数据集合:TaskNoteStateChangeHistory

在我的任务服务类中,我有一个@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"));
    }
  }
}

如您所见,我创建了一个任务对象并向其中添加了两个注释,然后尝试保存它。我假设保存任务是可行的,因为我可以让它们显示在我的视图中。但是,备注列表始终为空。

我需要做什么才能将子数据与父数据一起保存?

【问题讨论】:

  • 在@OneToMany注解中添加'cascade'属性会解决问题吗? cascade=CascadeType.ALL

标签: java spring-boot spring-data-jpa


【解决方案1】:

如其他答案所述,您必须在 Task notes 上使用 @OneToMany(cascade=CascadeType.ALL)

要使代码正常工作,您必须设置双向实体关系:

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 注释。其实MERGEPERSIST 就足够了,但我认为如果你级联你应该级联所有。

    【讨论】:

      【解决方案3】:

      解决方案

      您可以通过在@OneToMany 注释中添加cascade= CascadeType.ALL 作为属性来解决它。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-05-10
        • 2019-09-30
        • 1970-01-01
        • 1970-01-01
        • 2020-09-26
        • 2019-10-01
        • 2014-12-06
        相关资源
        最近更新 更多