猿问

为什么 hibernate 创建空外键?

我正在尝试创建一个包含两个实体的 Spring Boot 应用程序:Question 和 QuestionChoices。我正在使用双向一对多关系。当我尝试创建一个 Question 实体以及 QuestionChoices 列表时,QuestionChoice 中的外键为空。


这是我的 QuestionChoice 实体:


@Entity

@Data

@NoArgsConstructor

@AllArgsConstructor

public class QuestionChoice {


    @Id

    @GeneratedValue(strategy = GenerationType.IDENTITY)

    private int id;


    private String choice;


    @ManyToOne

    @JoinColumn(name = "question_id")

    private Question question;


    public QuestionChoice(String choice, Question question) {

        this.choice = choice;

        this.question = question;

    }


    public QuestionChoice(String choice) {

        this.choice = choice;

    }


}

这是我的问题实体:


@Entity

@Data

@NoArgsConstructor

@AllArgsConstructor

public class Question {


    @Id

    @GeneratedValue(strategy = GenerationType.IDENTITY)

    private int question_id;

    private String questionName;

    private String questionText;


    @OneToMany(mappedBy = "question", cascade = CascadeType.ALL)

    private List<QuestionChoice> questionChoices;


    public Question(String questionName, String questionText, List<QuestionChoice> questionChoices) {

        this.questionName = questionName;

        this.questionText = questionText;

        this.questionChoices = questionChoices;

        this.questionChoices.forEach(x -> x.setQuestion(this));

    }

}

我有一个 QuestionRepository 和 QuestionChoiceRepository:


@Repository

public interface QuestionRepository extends JpaRepository<Question, Integer> {

}


@Repository

public interface QuestionChoiceRepository extends JpaRepository<QuestionChoice, Integer> {

}

这是我的控制器:


@RestController

public class Controller {


    QuestionRepository questionRepository;

    QuestionChoiceRepository questionChoiceRepository;


    public Controller(QuestionRepository questionRepository,

                      QuestionChoiceRepository questionChoiceRepository) {

        this.questionRepository = questionRepository;

        this.questionChoiceRepository = questionChoiceRepository;

    }


杨魅力
浏览 129回答 3
3回答

MMMHUHU

questionChoices您正在JSON 正文中发送一个字符串数组。List<Question>您的 JSON 映射器需要从该字符串数组中填充 a 。所以它需要将每一个都转化String为一个QuestionChoice对象。据推测,它是通过调用QuestionChoice以 aString作为参数的构造函数来实现的。因此,您正在保存一个Question其中QuestionChoices所有属性都为空的question属性。因此,您告诉 JPA 所有 QuestionChoices 都没有任何问题(因为它为空)。因此,JPA 会保存您告诉它保存的内容:QuestionChoices,而没有任何父问题。您需要正确question初始化QuestionChoice.

跃然一笑

反序列化器将始终使用默认构造函数来构造对象。您的自定义构造函数对反序列化没有影响。你能做的是:1 - 保证服务/控制器层中的关联@PostMapping("/question")public Question createQuestion(@RequestBody Question question) {&nbsp; &nbsp; question.getQuestionChoices().forEach(choice -> choice.setQuestion(question));&nbsp; &nbsp; return questionRepository.save(question);}或 2 - 保证 setter 方法中的关联:public class Question {&nbsp; &nbsp; // omitted for brevity&nbsp; &nbsp; @OneToMany(mappedBy = "question", cascade = CascadeType.ALL)&nbsp; &nbsp; private List<QuestionChoice> questionChoices;&nbsp; &nbsp; public void setQuestionChoices(List<QuestionChoice> questionChoices) {&nbsp; &nbsp; &nbsp; &nbsp; if (questionChoices != null) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; questionChoices.forEach(choice -> choice.setQuestion(this));&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; this.questionChoices = questionChoices;&nbsp; &nbsp; }}更新为了防止无限递归,只需从“questionChoice”中删除“question”属性以进行演示。我可以想到两个选择:1 - 将question内部设置为 nullquestionChoice@PostMapping("/question")public Question createQuestion(@RequestBody Question question) {&nbsp; &nbsp; Question savedQuestion = questionRepository.save(question);&nbsp; &nbsp; savedQuestion.getQuestionChoices().forEach(choice -> choice.setQuestion(null));&nbsp; &nbsp; return savedQuestion;}@GetMapping("/question")public List<Question> getQuestions() {&nbsp; &nbsp; List<Question> questions questionRepository.findAll();&nbsp; &nbsp; questions.forEach(question -> {&nbsp; &nbsp; &nbsp; &nbsp; question.getQuestionChoices.forEach(choice -> choice.setQuestion(null));&nbsp; &nbsp; });&nbsp; &nbsp; return questions;}这会将您的问题选择和外键保存到数据库中,但questionChoices.question在发送响应时将序列化为 null 以防止无限递归。2 - 使用 DTO。您创建 DTO 将它们序列化为响应对象,以准确返回您想要的内容。QuestionDTO.javapublic class QuestionDTO {&nbsp; &nbsp; private int question_id;&nbsp; &nbsp; private String questionName;&nbsp; &nbsp; private String questionText;&nbsp; &nbsp; // notice that here you're using composition of DTOs (QuestionChoiceDTO instead of QuestionChoice)&nbsp; &nbsp; private List<QuestionChoiceDTO> questionChoices;&nbsp; &nbsp; // constructors..&nbsp; &nbsp; // getters and setters..}QuestionChoiceDTO.javapublic class QuestionChoiceDTO {&nbsp; &nbsp; private int id;&nbsp; &nbsp; private String choice;&nbsp; &nbsp; // notice that you don't need to create the Question object here&nbsp; &nbsp; // constructors..&nbsp; &nbsp; // getters and setters..}然后在你的控制器中:@PostMapping("/question")public QuestionDTO createQuestion(@RequestBody Question question) {&nbsp; &nbsp; Question savedQuestion = questionRepository.save(question);&nbsp; &nbsp; List<QuestionChoiceDTO> questionChoices = new ArrayList<>();&nbsp; &nbsp; savedQuestion.getQuestionChoices().forEach(choice -> {&nbsp; &nbsp; &nbsp; &nbsp; questionChoices.add(new QuestionChoiceDTO(choice.getId(), choice.getChoice()));&nbsp; &nbsp; });&nbsp; &nbsp; QuestionDTO response = new QuestionDTO(savedQuestion.getQuestion_id(), savedQuestion.getQuestionName(), savedQuestion.getQuestionText(), questionChoices);&nbsp; &nbsp; return response;}@GetMapping("/question")public List<QuestionDTO> getQuestions() {&nbsp; &nbsp; List<Question> questions = questionRepository.findAll();&nbsp; &nbsp; List<QuestionDTO> response = new ArrayList<>();&nbsp; &nbsp; questions.forEach(question -> {&nbsp; &nbsp; &nbsp; &nbsp; List<QuestionChoicesDTO> questionChoices = new ArrayList<>();&nbsp; &nbsp; &nbsp; &nbsp; question.getQuestionChoices().forEach(choice -> questionChoices.add(new QuestionChoiceDTO(choice.getId(), choice.getChoice()));&nbsp; &nbsp; &nbsp; &nbsp; responses.add(new QuestionDTO(savedQuestion.getQuestion_id(), savedQuestion.getQuestionName(), savedQuestion.getQuestionText(), questionChoices));&nbsp; &nbsp; });}我总是更喜欢后者,因为对于大型项目,恕我直言,使用 DTO 可以成为组织代码和简洁使用请求/响应对象而不使用域对象的强大工具。

BIG阳

您在请求后不使用构造函数 public Question(...) 。你应该制定一种方法将选择与问题联系起来
随时随地看视频慕课网APP

相关分类

Java
我要回答