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

Spring Boot一对多映射POST API报'tbl_services_id'不能为空错误

解决子服务保存时tbl_services_id不能为空的问题

看起来你遇到的是双向一对多关联下,子服务保存时外键字段为空的典型问题,我来帮你拆解原因并给出具体的修复方案:

问题根源分析

从你提供的信息来看,核心问题有两个:

  1. JSON反序列化失败:SubService实体的service字段加了@JsonIgnore,这会让Jackson在反序列化时直接忽略这个字段,导致前端传递的service数据无法绑定到对象上,最终theSubService.getService()为null。
  2. 前端请求载荷错误:你收到的JSON里service=null,即使没有@JsonIgnore,也无法关联到已存在的服务实例,数据库自然会报外键非空的错误。

具体修复方案

1. 调整双向关联的JSON序列化注解

@JsonIgnore会完全阻断该字段的序列化和反序列化,对于双向关联,我们应该用@JsonManagedReference和@JsonBackReference来处理,既避免循环引用,又能正确处理关联对象的反序列化:

修改Service实体的subServices字段:

@OneToMany(mappedBy="service", cascade= {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.DETACH, CascadeType.REFRESH})
@JsonManagedReference // 标记为主关联方,序列化时会包含子服务列表
private List<SubService> subServices;

修改SubService实体的service字段:

@ManyToOne( cascade= {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.DETACH, CascadeType.REFRESH})
@JoinColumn(name="tbl_services_id")
@JsonBackReference // 标记为从关联方,序列化时忽略,反序列化时可接收服务对象
private Service service;

2. 修正前端请求的JSON格式

现在前端需要传递关联服务的标识(比如已存在的服务ID),而不是null。你可以传递包含id的嵌套service对象,示例如下:

{
  "subServiceName": "GST",
  "subServiceDesc": "GST is a subservice which currently having",
  "subServiceImage": "path-to-image",
  "service": {
    "id": 1 // 替换为实际存在的服务ID
  }
}

3. 优化DAO层的保存逻辑(推荐)

为了确保关联的Service是数据库中的持久化实例(避免游离对象导致的异常),建议在保存子服务前,先从数据库查询对应的服务实例,再绑定到子服务上:

修改SubServicesHibernateDAOImpl的saveSubService方法:

@Override
public void saveSubService(SubService theSubService) {
    Session currentSession = entityManager.unwrap(Session.class);
    
    // 处理关联的服务实例:如果传递了服务ID,先查询持久化对象
    if (theSubService.getService() != null && theSubService.getService().getId() > 0) {
        Service persistentService = currentSession.get(Service.class, theSubService.getService().getId());
        theSubService.setService(persistentService);
    }
    
    System.out.println("SubService object : "+theSubService);
    currentSession.saveOrUpdate(theSubService);
}

可选方案:添加临时字段接收服务ID

如果不想修改前端的请求格式,也可以在SubService中添加一个不映射到数据库的临时字段,用来接收前端传递的服务ID:

@Entity
@Table(name="tbl_sub_services")
public class SubService {
    // 其他字段保持不变
    
    @Transient // 标记为非数据库字段,仅用于接收前端参数
    private Integer serviceId;

    // 对应的getter和setter
    
    // 原有service字段的getter和setter
}

然后在DAO层处理:

@Override
public void saveSubService(SubService theSubService) {
    Session currentSession = entityManager.unwrap(Session.class);
    
    // 通过临时字段serviceId查询服务实例
    if (theSubService.getServiceId() != null) {
        Service persistentService = currentSession.get(Service.class, theSubService.getServiceId());
        theSubService.setService(persistentService);
    }
    
    System.out.println("SubService object : "+theSubService);
    currentSession.saveOrUpdate(theSubService);
}

这样前端可以直接传递"serviceId": 1,无需嵌套对象。


内容的提问来源于stack exchange,提问作者Sandip Armal Patil

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 08:20:36