SpringBoot REST API中PUT/PATCH更新Score文档失败求助
我来帮你排查下问题,你遇到的两个问题本质上都是对Spring Data MongoDB的save方法逻辑和实体更新流程理解有偏差,咱们一步步解决:
问题1:PUT方法保存新实例而非更新现有数据
你当前的PUT方法直接保存了传入的score1对象,但Spring Data MongoDB的save()方法是根据实体的@Id字段判断是新增还是更新的:如果传入实体的@Id(也就是你的score字段)不存在于数据库中,就会插入新文档;只有当@Id匹配现有文档时,才会执行更新。
你的代码里有两个核心问题:
- 没有把传入的更新字段赋值到从数据库查到的
existingScore上,而是直接保存了新的score1对象 - 处理
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无法自动解析这种方法签名,所以会抛出找不到对应方法的异常。
解决步骤:
- 删除Repository中自定义的save方法,使用MongoRepository自带的
save(T entity)方法即可 - 在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
相关产品推荐
相关产品推荐

