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

SpringBoot REST API中PUT/PATCH更新Score文档失败求助

我来帮你排查下问题,你遇到的两个问题本质上都是对Spring Data MongoDB的save方法逻辑和实体更新流程理解有偏差,咱们一步步解决:


问题1:PUT方法保存新实例而非更新现有数据

你当前的PUT方法直接保存了传入的score1对象,但Spring Data MongoDB的save()方法是根据实体的@Id字段判断是新增还是更新的:如果传入实体的@Id(也就是你的score字段)不存在于数据库中,就会插入新文档;只有当@Id匹配现有文档时,才会执行更新。

你的代码里有两个核心问题:

  1. 没有把传入的更新字段赋值到从数据库查到的existingScore上,而是直接保存了新的score1对象
  2. 处理history时直接新建了列表,会覆盖原有的历史记录(如果有的话)

修正后的PUT方法代码:

import org.springframework.http.HttpStatus;
import java.util.ArrayList;
import java.util.Date;

//Update Score By ID
@PutMapping("/{scoreId}")
public ResponseEntity<?> updatePlayerByID(
        @PathVariable("scoreId") String scoreId,
        @RequestBody @JsonView(Views.class) @Valid Score updatedScore) {
    // 根据@Id(score字段)查找现有记录
    Score existingScore = srepo.findByScore(scoreId);
    if (existingScore == null) {
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Score not found!");
    }

    // 把传入的更新字段同步到现有实体上(按需选择要更新的字段)
    existingScore.setPlayer(updatedScore.getPlayer());
    existingScore.setCode(updatedScore.getCode());
    existingScore.setDate(updatedScore.getDate() != null ? updatedScore.getDate() : new Date());

    // 处理历史记录:复用原有列表,添加新记录
    History newHistoryEntry = new History();
    newHistoryEntry.setScore(updatedScore.getScore());
    newHistoryEntry.setDate(new Date()); // 记录更新时间,也可以用传入的date

    if (existingScore.getHistory() == null) {
        existingScore.setHistory(new ArrayList<>());
    }
    existingScore.getHistory().add(newHistoryEntry);

    // 保存更新后的实体:此时@Id匹配数据库中的文档,会执行更新而非新增
    Score savedScore = srepo.save(existingScore);
    return ResponseEntity.ok(savedScore);
}

问题2:@PatchMapping的PropertyReferenceException报错

你在ScoreRepository中自定义的save(ScoreDto scoredto, String score)方法是无效的,Spring Data MongoDB无法自动解析这种方法签名,所以会抛出找不到对应方法的异常。

解决步骤:

  1. 删除Repository中自定义的save方法,使用MongoRepository自带的save(T entity)方法即可
  2. 在Patch方法中手动将DTO的字段映射到从数据库查到的实体上,再执行保存

修正后的ScoreRepository:

@Repository
public interface ScoreRepository extends MongoRepository<Score, String>{
    Score findByScore(String score);
    // 移除自定义的save方法,使用父接口的save方法
}

修正后的@PatchMapping方法:

@PatchMapping("/{scoreId}")
public ResponseEntity<?> updateByScore(
        @PathVariable("scoreId") String scoreId,
        @RequestBody ScoreDto scoreDto) {
    Score existingScore = srepo.findByScore(scoreId);
    if (existingScore == null) {
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Score not found!");
    }

    // 根据DTO的字段更新实体(这里以更新score字段并添加历史记录为例)
    if (scoreDto.getScore() != null) {
        // 更新当前score字段
        existingScore.setScore(scoreDto.getScore());

        // 添加到历史记录列表
        History newHistory = new History();
        newHistory.setScore(scoreDto.getScore());
        newHistory.setDate(new Date());

        if (existingScore.getHistory() == null) {
            existingScore.setHistory(new ArrayList<>());
        }
        existingScore.getHistory().add(newHistory);
    }

    // 如果DTO有其他需要更新的字段,在这里继续处理

    Score savedScore = srepo.save(existingScore);
    return ResponseEntity.ok(savedScore);
}

额外注意点

  • 你把业务字段score作为@Id,这可能会带来麻烦:如果更新score字段,文档的_id也会跟着改变,后续查询可能出现问题。建议单独使用一个独立的id字段作为@Id(比如String id;),把score作为普通业务字段。
  • 处理history列表时,一定要复用原有的列表,不要直接新建列表覆盖,否则会丢失之前的历史记录。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 15:22:42