You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot中OneToMany关联场景下父子实体保存更新问题求助

问题根因说明

  1. 第一种方案保存后office为空:双向OneToMany关联中,mappedBy = "compania"指定了关联关系的维护方是Office实体的compania字段,你只把Office加入了Compania的集合,没有给Office设置compania属性,导致数据库中office表的compania_id字段为空,查询时自然关联不到数据。另外你原有代码中变量名拼写错误,把查询到的companiaFound写成了未定义的companiaEnc,也是保存失败的直接原因。
  2. 第二种方案递归报错:JSON序列化时,Compania序列化其office集合,每个Office对象又会序列化其compania属性,形成无限循环导致栈溢出。

修复步骤

1. 修正实体类配置

处理JSON序列化递归问题

在关联字段上添加Jackson注解打断循环,二选一即可:

  • 方案A:在Office的compania字段上加@JsonIgnore,序列化Office时直接忽略关联的Compania对象
  • 方案B:分别在父子实体的关联字段上加配对注解,适合需要在部分场景返回关联信息的情况:
    // Compania类的office字段加
    @JsonManagedReference
    @OneToMany(
        mappedBy = "compania",
        cascade = CascadeType.ALL,
        fetch = FetchType.LAZY,
        orphanRemoval = true
    )
    private List<Office> office = new ArrayList<>(); // 记得初始化集合避免空指针
    
    // Office类的compania字段加
    @JsonBackReference
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "compania_id", referencedColumnName = "id")
    private Compania compania;
    

封装关联维护方法

在Compania类中添加关联操作的工具方法,避免每次手动维护两侧关联时漏写:

// 添加Office
public void addOffice(Office office) {
    office.setCompania(this);
    this.office.add(office);
}

// 移除Office
public void removeOffice(Office office) {
    office.setCompania(null);
    this.office.remove(office);
}

功能实现示例

POST新增Office

@Transactional
public Office addOffice(Long companiaId, Office officeReq) {
    // 查询父实体,不存在抛出业务异常
    Compania compania = companiaRepository.findById(companiaId)
            .orElseThrow(() -> new RuntimeException("公司不存在,id:" + companiaId));
    // 重置id,避免前端传id导致意外更新已有数据
    officeReq.setId(null);
    // 用封装方法添加关联,自动维护两侧关系
    compania.addOffice(officeReq);
    // 保存父实体,cascade=ALL配置会自动级联保存新的Office
    Compania savedCompania = companiaRepository.save(compania);
    // 返回新增的Office对象
    return savedCompania.getOffice().get(savedCompania.getOffice().size() - 1);
}

PUT编辑已有Office

@Transactional
public Office updateOffice(Long companiaId, Long officeId, Office officeReq) {
    // 查询父实体
    Compania compania = companiaRepository.findById(companiaId)
            .orElseThrow(() -> new RuntimeException("公司不存在,id:" + companiaId));
    // 查询要编辑的Office,校验是否属于当前公司
    Office existOffice = officeRepository.findById(officeId)
            .filter(o -> o.getCompania().getId().equals(companiaId))
            .orElseThrow(() -> new RuntimeException("办公点不存在或不属于当前公司,id:" + officeId));
    // 更新可编辑字段
    existOffice.setTitle(officeReq.getTitle());
    existOffice.setName(officeReq.getName());
    existOffice.setPath(officeReq.getPath());
    existOffice.setIdRef(officeReq.getIdRef());
    // 保存更新
    return officeRepository.save(existOffice);
}

注意事项

  • 所有涉及数据库读写的方法必须加@Transactional注解,避免懒加载失败、事务未提交等问题
  • 不要直接调用Optional.get(),必须处理查询结果为空的场景
  • Office类的id字段上加了@JsonIgnore,如果需要将id返回给前端可以移除该注解

内容的提问来源于stack exchange,提问作者Varox

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 12:54:00