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

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列表

  1. 调整Path实体类,将intermediatePoints替换为存储ID的列表:
    // 替换原@DBRef字段
    private List<String> intermediatePointIds = new ArrayList<>();
    
  2. 在服务层通过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);
    
  3. 前端请求JSON修改为:
    "intermediatePointIds": ["640a263e585b0232fee17908"]
    

方法二:配置Jackson解析MongoDB原生DBRef格式

  1. 确保引入Spring Data MongoDB依赖(版本匹配Spring Boot):
    <!-- Maven示例 -->
    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-mongodb</artifactId>
    </dependency>
    
  2. 自定义Jackson配置,注册MongoDB专用模块:
    @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;
        }
    }
    
    配置后Jackson可自动识别$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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 17:54:56