Spring Boot一对多映射POST API报'tbl_services_id'不能为空错误
解决子服务保存时
tbl_services_id不能为空的问题 看起来你遇到的是双向一对多关联下,子服务保存时外键字段为空的典型问题,我来帮你拆解原因并给出具体的修复方案:
问题根源分析
从你提供的信息来看,核心问题有两个:
- JSON反序列化失败:
SubService实体的service字段加了@JsonIgnore,这会让Jackson在反序列化时直接忽略这个字段,导致前端传递的service数据无法绑定到对象上,最终theSubService.getService()为null。 - 前端请求载荷错误:你收到的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
相关产品推荐
相关产品推荐

