3 回答
TA贡献1834条经验 获得超8个赞
questionChoices
您正在JSON 正文中发送一个字符串数组。List<Question>
您的 JSON 映射器需要从该字符串数组中填充 a 。所以它需要将每一个都转化String
为一个QuestionChoice
对象。据推测,它是通过调用QuestionChoice
以 aString
作为参数的构造函数来实现的。
因此,您正在保存一个Question
其中QuestionChoices
所有属性都为空的question
属性。因此,您告诉 JPA 所有 QuestionChoices 都没有任何问题(因为它为空)。因此,JPA 会保存您告诉它保存的内容:QuestionChoices,而没有任何父问题。
您需要正确question
初始化QuestionChoice
.
TA贡献1826条经验 获得超6个赞
反序列化器将始终使用默认构造函数来构造对象。您的自定义构造函数对反序列化没有影响。
你能做的是:
1 - 保证服务/控制器层中的关联
@PostMapping("/question")
public Question createQuestion(@RequestBody Question question) {
question.getQuestionChoices().forEach(choice -> choice.setQuestion(question));
return questionRepository.save(question);
}
或 2 - 保证 setter 方法中的关联:
public class Question {
// omitted for brevity
@OneToMany(mappedBy = "question", cascade = CascadeType.ALL)
private List<QuestionChoice> questionChoices;
public void setQuestionChoices(List<QuestionChoice> questionChoices) {
if (questionChoices != null) {
questionChoices.forEach(choice -> choice.setQuestion(this));
}
this.questionChoices = questionChoices;
}
}
更新
为了防止无限递归,只需从“questionChoice”中删除“question”属性以进行演示。
我可以想到两个选择:
1 - 将question内部设置为 nullquestionChoice
@PostMapping("/question")
public Question createQuestion(@RequestBody Question question) {
Question savedQuestion = questionRepository.save(question);
savedQuestion.getQuestionChoices().forEach(choice -> choice.setQuestion(null));
return savedQuestion;
}
@GetMapping("/question")
public List<Question> getQuestions() {
List<Question> questions questionRepository.findAll();
questions.forEach(question -> {
question.getQuestionChoices.forEach(choice -> choice.setQuestion(null));
});
return questions;
}
这会将您的问题选择和外键保存到数据库中,但questionChoices.question在发送响应时将序列化为 null 以防止无限递归。
2 - 使用 DTO。
您创建 DTO 将它们序列化为响应对象,以准确返回您想要的内容。
QuestionDTO.java
public class QuestionDTO {
private int question_id;
private String questionName;
private String questionText;
// notice that here you're using composition of DTOs (QuestionChoiceDTO instead of QuestionChoice)
private List<QuestionChoiceDTO> questionChoices;
// constructors..
// getters and setters..
}
QuestionChoiceDTO.java
public class QuestionChoiceDTO {
private int id;
private String choice;
// notice that you don't need to create the Question object here
// constructors..
// getters and setters..
}
然后在你的控制器中:
@PostMapping("/question")
public QuestionDTO createQuestion(@RequestBody Question question) {
Question savedQuestion = questionRepository.save(question);
List<QuestionChoiceDTO> questionChoices = new ArrayList<>();
savedQuestion.getQuestionChoices().forEach(choice -> {
questionChoices.add(new QuestionChoiceDTO(choice.getId(), choice.getChoice()));
});
QuestionDTO response = new QuestionDTO(savedQuestion.getQuestion_id(), savedQuestion.getQuestionName(), savedQuestion.getQuestionText(), questionChoices);
return response;
}
@GetMapping("/question")
public List<QuestionDTO> getQuestions() {
List<Question> questions = questionRepository.findAll();
List<QuestionDTO> response = new ArrayList<>();
questions.forEach(question -> {
List<QuestionChoicesDTO> questionChoices = new ArrayList<>();
question.getQuestionChoices().forEach(choice -> questionChoices.add(new QuestionChoiceDTO(choice.getId(), choice.getChoice()));
responses.add(new QuestionDTO(savedQuestion.getQuestion_id(), savedQuestion.getQuestionName(), savedQuestion.getQuestionText(), questionChoices));
});
}
我总是更喜欢后者,因为对于大型项目,恕我直言,使用 DTO 可以成为组织代码和简洁使用请求/响应对象而不使用域对象的强大工具。
添加回答
举报