Spring Boot @OneToMany关联问题:无法创建新闪卡数据
一对多关联下创建闪卡的序列化循环问题
开发简易闪卡项目时,用户与闪卡为一对多(@OneToMany)关联,创建新闪卡时出现如下报错:
2023-08-27T09:03:14.399+02:00 WARN 31571 --- [nio-8080-exec-5] .w.s.m.s.DefaultHandlerExceptionResolver : Failure while trying to resolve exception [org.springframework.http.converter.HttpMessageNotWritableException] java.lang.IllegalStateException: Cannot call sendError() after the response has been committed at org.apache.catalina.connector.ResponseFacade.checkCommitted(ResponseFacade.java:503) ~[tomcat-embed-core-10.1.11.jar:10.1.11] at org.apache.catalina.connector.ResponseFacade.sendError(ResponseFacade.java:347) ~[tomcat-embed-core-10.1.11.jar:10.1.11] ...
相关代码如下:
User类
@Data @Entity public class User { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "userId_generator") private Integer id; private String username; private String firstName; private String lastName; private String password; @OneToMany(fetch = FetchType.LAZY, mappedBy = "user") private List<Flashcard> flashcards; @CreationTimestamp private Instant createdOn; @UpdateTimestamp private Instant updatedOn; }
Flashcard类
@Entity @Data public class Flashcard { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "flashcardId_generator") private int id; private String name; private String description; @ManyToOne(fetch = FetchType.LAZY, optional = false) @JoinColumn(name = "user_id", nullable = false) @OnDelete(action = OnDeleteAction.CASCADE) private User user; }
FlashcardController类
@RestController public class FlashcardController { @Autowired FlashcardRepository flashcardRepository; @Autowired UserRepository userRepository; // 其他GET方法代码 @PostMapping("/users/{id}/flashcards") public ResponseEntity<Flashcard> createFlashcard(@PathVariable("id") int userId, @RequestBody Flashcard flashcard) { Flashcard _flashcard = userRepository.findById(userId).map(user -> { flashcard.setUser(user); return flashcardRepository.save(flashcard); }).get(); return new ResponseEntity<>(_flashcard, HttpStatus.CREATED); } }
现象
删除User类中的以下代码后,可正常创建闪卡,但无法通过用户ID查询其名下所有闪卡:
@OneToMany(fetch = FetchType.LAZY, mappedBy = "user") private List<Flashcard> flashcards;
问题原因
双向关联引发序列化循环:Spring MVC返回Flashcard响应时需要序列化该对象,而Flashcard包含User对象,User又关联List<Flashcard>,触发无限循环序列化,抛出HttpMessageNotWritableException。Tomcat后续尝试发送错误响应时,发现响应已提交,因此出现Cannot call sendError() after the response has been committed警告。
解决方案
方案1:忽略关联字段的序列化
在User类的flashcards字段上添加@JsonIgnore注解,避免序列化时触发循环:
@OneToMany(fetch = FetchType.LAZY, mappedBy = "user") @JsonIgnore private List<Flashcard> flashcards;
方案2:使用Jackson双向关联注解
用@JsonManagedReference和@JsonBackReference标记双向关联,明确序列化层级:
- 在User类的
@OneToMany字段上添加@JsonManagedReference - 在Flashcard类的
@ManyToOne字段上添加@JsonBackReference
修改后的User类字段:
@OneToMany(fetch = FetchType.LAZY, mappedBy = "user") @JsonManagedReference private List<Flashcard> flashcards;
修改后的Flashcard类字段:
@ManyToOne(fetch = FetchType.LAZY, optional = false) @JoinColumn(name = "user_id", nullable = false) @OnDelete(action = OnDeleteAction.CASCADE) @JsonBackReference private User user;
方案3:优化Controller的异常处理
原代码中使用get()方法获取Optional值,若用户不存在会抛出NoSuchElementException,建议替换为orElseThrow主动抛出异常,避免后续响应提交问题:
@PostMapping("/users/{id}/flashcards") public ResponseEntity<Flashcard> createFlashcard(@PathVariable("id") int userId, @RequestBody Flashcard flashcard) { Flashcard _flashcard = userRepository.findById(userId) .map(user -> { flashcard.setUser(user); return flashcardRepository.save(flashcard); }) .orElseThrow(() -> new RuntimeException("未找到ID为" + userId + "的用户")); return new ResponseEntity<>(_flashcard, HttpStatus.CREATED); }
内容的提问来源于stack exchange,提问作者emre.krn
相关产品推荐
相关产品推荐

