【问题标题】:Save an entity and all its related entities in a single save in spring boot在 Spring Boot 中一次保存一个实体及其所有相关实体
【发布时间】:2017-10-30 23:35:39
【问题描述】:

我正在使用 Spring Boot、REST 和 JPA 来构建我的应用程序。在应用程序中,有 2 个实体具有一对多关系。

实体 1:

@Entity
@Table( name = "report")
@JsonIgnoreProperties(ignoreUnknown = true)
public class CustomReport {

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "REPORT_SEQ")
@SequenceGenerator(sequenceName = "REPORT_SEQ", allocationSize = 1, name = "REPORT_SEQ")
private Long id;

private String name;
private Long createdBy;
private Timestamp lastModifiedTimestamp;


@OneToMany(mappedBy = "customReport", cascade = CascadeType.ALL)
private Set<CustomReportActivity> customReportActivitySet;



public Set<CustomReportActivity> getCustomReportActivitySet() {
    return customReportActivitySet;
}

public void setCustomReportActivitySet(Set<CustomReportActivity> customReportActivitySet) {
    this.customReportActivitySet = customReportActivitySet;
}



public Long getId() {
    return id;
}

public void setId(Long id) {
    this.id = id;
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

public Long getCreatedBy() {
    return createdBy;
}

public void setCreatedBy(Long createdBy) {
    this.createdBy = createdBy;
}

public Timestamp getLastModifiedTimestamp() {
    return lastModifiedTimestamp;
}

public void setLastModifiedTimestamp(Timestamp lastModifiedTimestamp) {
    this.lastModifiedTimestamp = lastModifiedTimestamp;
}

}

实体 2:

@Entity
@Table( name = "report_activity")
public class CustomReportActivity {

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "REPORT_ACTIVITY_SEQ")
@SequenceGenerator(sequenceName = "REPORT_ACTIVITY_SEQ", allocationSize = 1, name = "REPORT_ACTIVITY_SEQ")
private Long id;

String activityName;

@ManyToOne
@JoinColumn( name="report_id" )
@JsonBackReference
private CustomReport customReport;

public Long getId() {
    return id;
}

public void setId(Long id) {
    this.id = id;
}

public String getActivityName() {
    return activityName;
}

public void setActivityName(String activityName) {
    this.activityName = activityName;
}

public CustomReport getCustomReport() {
    return customReport;
}

public void setCustomReport(CustomReport customReport) {
    this.customReport = customReport;
}

}

我的请求 JSON 如下:

{
   "name": "test report",
   "createdBy" : 129,
   "customReportActivitySet": [
        {"activityName":"a"},
        {"activityName":"b"},
        {"activityName":"c"},
        {"activityName":"d"},
        {"activityName":"e"}
    ]  
}

我想一次性保存两个实体。我通过以下方式实现了保存功能:

@RequestMapping(value="/save", method = RequestMethod.POST)
public ResponseEntity<?> addReport(@RequestBody CustomReport customReport) {
    return new ResponseEntity<>(customReportService.createCustomReport(customReport), HttpStatus.CREATED);

}

CustomReportService 方法:

 public CustomReport createCustomReport(CustomReport customReport) {
    return customReportRepository.save(customReport);
}

自定义存储库:

public interface CustomReportRepository extends CrudRepository<CustomReport, Long> {

}

但是我得到了约束违反异常:

java.sql.SQLIntegrityConstraintViolationException: ORA-01400: 不能 将 NULL 插入 ("REPORT_ACTIVITY"."REPORT_ID")

是否可以在一次保存操作中保存两个实体?

请帮忙!

【问题讨论】:

  • customReportService.createCustomReport 里面有什么?
  • @MaciejKowalski 我已经编辑了我的帖子。请检查
  • 请参考相关帖子:stackoverflow.com/questions/3927091/…
  • @MaciejKowalski 我正在创建另一种服务方法来填充子实体的所有属性。不知道我们可以通过在保存之前在子实体中添加自定义报表关系来做到这一点。你的解决方案奏效了。非常感谢!

标签: java spring jpa spring-boot one-to-many


【解决方案1】:

您必须添加一小段代码来填充CustomReport 实例中的每个CustomReportActivity。只有持久化提供才能成功执行级联保存操作:

public CustomReport createCustomReport(CustomReport customReport) {
   customReport.getCustomReportActivitySet.forEach((activity) -> {
      activity.setCustomReport(customReport);
   });

   return customReportRepository.save(customReport);
}

最重要的是,必须在关系的两边都设置依赖关系。

【讨论】:

  • 根据文档,CrudRepository.save(entity) 保存并更新实体。但是当我使用 save() 更新自定义报告实体时,我对 report_activity 表的主键约束被违反.report_activity 表的主键是(report_id,activity_name)。 JPA 似乎试图在 report_activity 表中插入一个新行,而不是更新现有行。请告诉我为什么它不做合并操作
  • 是的,隐式合并存在问题。我个人必须通过自己的合并操作来实现。
  • 哦,好吧。我期待 JPA 处理相关实体的合并。感谢您的澄清!
  • JPA 从不隐式处理(您必须自己调用合并).. spring-data-jpa 应该.. 但就像我说的那样,该功能存在一些问题
  • @MaciejKowalski 是对的,因为这是 bidirectional many-to-one association,所以您需要更新关联的双方。这是它不起作用的唯一原因。
【解决方案2】:

试试这个示例,在我的例子中,它按预期工作,子实体在单个保存操作中自动保存,并创建与父实体的关系:

@Entity
public class Parent {
    @Id
    private Long id;

    @JoinColumn(name = "parentId")
    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    private Set<Child> children;
}

@Entity
public class Child {
    @Id
    private Long id;
    private Long parentId;
}

【讨论】:

  • 它可能适合您的用例,但它与 OP 的用例不同,因为您使用的是 unidirectional one-to-many association 而不是 bidirectional association。因此,仅设置关联的一侧对您有用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-08
  • 2017-05-11
相关资源
最近更新 更多