Spring Data MongoDB插入Path时遇Cannot create a reference to an object with a NULL id报错求助
Spring Data MongoDB:解决"Cannot create a reference to an object with a NULL id"错误
问题场景
在使用Spring Data MongoDB插入Path文档时触发500错误,提示"Cannot create a reference to an object with a NULL id"。可正常创建PointPath文档,但插入指定Path请求JSON时出错。
相关代码
Controller代码
@PostMapping(value = "/insert", produces = "application/json") public ResponseEntity<?> createPath(@RequestBody Path path) { log.info("Insert of the path"); pathService.insertPath(path); return ResponseEntity.ok(path); }
PointPath实体类
@Data @Document(collection = "pointPath") @JsonInclude(JsonInclude.Include.NON_NULL) public class PointPath { @Id private String id; private String streetName; private String numberStreet; private String municipality; private Integer zipCode; private Double latitude; private Double longitude; }
Path实体类
@Data @Document(collection = "path") @JsonInclude(JsonInclude.Include.NON_NULL) public class Path { @Id private String id; private PointPath starterPoint; private PointPath endPoint; @DBRef private List<PointPath> intermediatePoints = new ArrayList<>(); private boolean circuit; // counting minuts private Integer time; // counting meters private Integer totalDistance; }
请求JSON
{ "starterPoint": { "streetName": "Via Roma", "numberStreet": "12", "municipality": "Roma", "zipCode": 22100, "latitude": 41.902782, "longitude": 12.496366 }, "endPoint": { "streetName": "Piazza del Popolo", "numberStreet": "", "municipality": "Roma", "zipCode": 22187, "latitude": 41.910732, "longitude": 12.476200 }, "intermediatePoints": [ { "$ref": "pointPath", "$id": "640a263e585b0232fee17908" } ], "circuit": false, "time": 30, "totalDistance": 6000 }
错误原因
错误核心在于intermediatePoints字段的处理:
- 该字段标记了
@DBRef,表示存储对PointPath文档的引用 - 前端传入的是MongoDB原生引用格式(含
$ref和$id),但Spring Data MongoDB默认的Jackson组件无法直接解析这种格式,会将其识别为普通PointPath对象 - 这个"伪"PointPath对象仅包含
id值,但Spring会误判为需要插入新文档,或在验证引用时因对象其他字段未正确映射,触发"id为NULL"的校验错误
解决方法
方法一:修改请求格式,直接传递ID列表
- 调整Path实体类,将
intermediatePoints替换为存储ID的列表:// 替换原@DBRef字段 private List<String> intermediatePointIds = new ArrayList<>(); - 在服务层通过ID查询对应PointPath实例,再关联到Path对象:
// pathService.insertPath方法内逻辑 List<PointPath> points = intermediatePointIds.stream() .map(pointPathRepository::findById) .filter(Optional::isPresent) .map(Optional::get) .collect(Collectors.toList()); path.setIntermediatePoints(points); - 前端请求JSON修改为:
"intermediatePointIds": ["640a263e585b0232fee17908"]
方法二:配置Jackson解析MongoDB原生DBRef格式
- 确保引入Spring Data MongoDB依赖(版本匹配Spring Boot):
<!-- Maven示例 --> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-mongodb</artifactId> </dependency> - 自定义Jackson配置,注册MongoDB专用模块:
配置后Jackson可自动识别@Configuration public class MongoJacksonConfig { @Bean public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() { MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.registerModule(new MongoJackson2Module()); converter.setObjectMapper(objectMapper); return converter; } }$ref和$id格式,转换为有效DBRef引用对象。
方法三:手动解析请求中的DBRef格式
在Controller层手动处理请求参数,转换为可用对象:
@PostMapping(value = "/insert", produces = "application/json") public ResponseEntity<?> createPath(@RequestBody Map<String, Object> requestBody) { log.info("Insert of the path"); ObjectMapper mapper = new ObjectMapper(); // 转换起点和终点 PointPath starterPoint = mapper.convertValue(requestBody.get("starterPoint"), PointPath.class); PointPath endPoint = mapper.convertValue(requestBody.get("endPoint"), PointPath.class); // 解析中间点引用,查询对应实例 List<Map<String, Object>> intermediateRefs = (List<Map<String, Object>>) requestBody.get("intermediatePoints"); List<PointPath> intermediatePoints = intermediateRefs.stream() .map(ref -> ref.get("$id").toString()) .map(id -> pointPathRepository.findById(id).orElseThrow(() -> new RuntimeException("PointPath不存在"))) .collect(Collectors.toList()); // 构建Path对象 Path path = new Path(); path.setStarterPoint(starterPoint); path.setEndPoint(endPoint); path.setIntermediatePoints(intermediatePoints); path.setCircuit((Boolean) requestBody.get("circuit")); path.setTime((Integer) requestBody.get("time")); path.setTotalDistance((Integer) requestBody.get("totalDistance")); pathService.insertPath(path); return ResponseEntity.ok(path); }
内容的提问来源于stack exchange,提问作者Pasqualone
相关产品推荐
相关产品推荐

