Hibernate JPA一对多无联接表

Hibernate JPA一对多无联接表,hibernate,jpa,orm,spring-data-jpa,hibernate-mapping,Hibernate,Jpa,Orm,Spring Data Jpa,Hibernate Mapping,我试图为测验数据库创建JPA实体类,在这里我有两个实体问题和选项 方法1 像这样从一个问题到另一个选项创建一个简单的关系 @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY) private Set<Option> options = new HashSet<>(); @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "questionId")

我试图为测验数据库创建JPA实体类,在这里我有两个实体问题和选项

方法1

像这样从一个问题到另一个选项创建一个简单的关系

@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<Option> options = new HashSet<>();
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "questionId")
private Question question;
它工作得很好,只是它创建了一个额外的表,在该表中管理问题选项和问题选项关系。此外,对于所有记录,选项都有questionId列及其null

我想避免那个额外的表,并想在选项表中填充questionid。所以在谷歌搜索了一下之后,我知道我需要使用mappedBy属性

方法2

问题实体

@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy="question")
private Set<Option> options = new HashSet<>();
@ManyToOne(fetch = FetchType.LAZY)
private Question question;
现在并没有创建联接表,而是将question_questionId列添加到选项表中,但它再次显示为null。因此,我的端点不会返回带有问题的选项

我希望我的问题是清楚的。如何在选项表中填充questionId

编辑

完整问题实体

@Entity
@Table(name="questions")
public class Question implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private int questionId;

    private String author;

    private boolean expired;

    private String questionText;

    //bi-directional many-to-one association to Option
    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy="question")
    private Set<Option> options = new HashSet<>();

    public Question() {
    }

    public int getQuestionId() {
        return this.questionId;
    }

    public void setQuestionId(int questionId) {
        this.questionId = questionId;
    }

    public String getAuthor() {
        return this.author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public boolean isExpired() {
        return expired;
    }

    public void setExpired(boolean expired) {
        this.expired = expired;
    }

    public String getQuestionText() {
        return this.questionText;
    }

    public void setQuestionText(String questionText) {
        this.questionText = questionText;
    }

    public Set<Option> getOptions() {
        return this.options;
    }

    public void setOptions(Set<Option> options) {
        this.options = options;
    }

    public Option addOption(Option option) {
        getOptions().add(option);
        option.setQuestion(this);

        return option;
    }

    public Option removeOption(Option option) {
        getOptions().remove(option);
        option.setQuestion(null);

        return option;
    }

}
}

存储库

@存储库
公共接口问题库扩展了Crudepository{
}
服务等级

@Autowired
私人问题库;
公共问题getQuestion(长id){
Question-Question=questionRepository.findOne(id);
Set options=question.getOptions();
options.forEach(s->s.setCorrectAnswer(false));
返回问题;
}
公开质询补充质询(质询){
返回questionRepository.save(问题);
}
控制器

@GetMapping
@RequestMapping(method=RequestMethod.GET,value=“/questions/{id}”)
公共响应属性getQuestion(@PathVariable long id){
返回新的ResponseEntity(questionService.getQuestion(id),HttpStatus.OK);
}
@邮戳
@RequestMapping(method=RequestMethod.POST,value=“/questions”)
@交易的
公共响应性addQuestion(@RequestBody-Question-Question){
logger.info(“从客户端收到的请求:+question.toString());
返回新的ResponseEntity(questionService.addQuestion(question),HttpStatus.OK);
}


我认为您的
添加问题
方法应该如下所示:

public Question addQuestion(Question question) {
   Question newQuestion = questionRepository.save(question);
   question.getQuestions().forEach(option -> {
      Option newOption = new Option();
      newOption.setQuestion(newQuestion); // need to reference managed (attached to JPA session) entity
      newOption.setOptionText(option.getOptionText());
      newOption.setCorrectAnswer(Optional.ofNullable(option.isCorrectAnswer()).orElse(false)));
      newQuestion.getOptions().add(optionRepository.save(newOption));
   });

   // it's done implicitly here because your controller's (or service's) method 
   // is marked with @Transactional, otherwise it must be done explicitly
   // newQuestion = questionRepository.save(newQuestion);

   return newQuestion;
}

映射在第二种方法中似乎是正确的,因此您应该在创建问题实体、选项实体以及在它们之间建立关系的地方发布一些代码。正如任何JPA文档所告诉您的,
mappedBy
是将关系标记为双向的,并将双方关联起来。这些文档会说,
@JoinColumn
将其定义为不使用联接表。这有多难?要填充questionId,只需设置选项实体的问题实体。JPA将从中读取id并将其保存在数据库中。因此,您必须确保您首先保留了问题实体,以便它有一个id。这已经被问过无数次了。第一个映射是错误的:它定义了两个独立的关联,而不是双向关联。第二个映射是正确的。如果questionId为null,则表示您忘记将option.question设置为非null值。将选项添加到集合中是不够的:您还需要设置选项的问题。阅读指南:@Nikolay Shevchenko我在这里添加了完整的代码Shevchenko感谢您查看此代码。我尝试了此代码,但它在选项表中创建了重复条目。当我们执行第一个save语句时,它会在选项表中创建一条记录,其中questionId为null;当我们执行最后一个save ie optionRepository.save时,它会在填充question id的位置插入新记录。我如何更新它而不是创建新的?我应该手动还是jpa中有现成的解决方案?再次感谢尤舍甫琴科:当我以单向方式连接表时,它起作用了@OneToMany(cascade=CascadeType.ALL,fetch=FetchType.LAZY)。@JoinColumn(name=“questionId”)private Set options=new HashSet();
@Repository
public interface QuestionRepository extends CrudRepository<Question,Long>{

}
@Autowired
    private QuestionRepository questionRepository;
    public Question getQuestion(Long id) {
        Question  question= questionRepository.findOne(id);
        Set<Option> options = question.getOptions();
        options.forEach(s -> s.setCorrectAnswer(false));
        return question;
    }

    public Question addQuestion(Question question) {
        return questionRepository.save(question);
    }
@GetMapping
    @RequestMapping(method = RequestMethod.GET, value="/questions/{id}")
    public ResponseEntity<Question> getQuestion(@PathVariable long id) {
        return new ResponseEntity<Question>(questionService.getQuestion(id),HttpStatus.OK);
    }

    @PostMapping
    @RequestMapping(method = RequestMethod.POST, value= "/questions")
    @Transactional
    public ResponseEntity<Question> addQuestion(@RequestBody Question question) {
        logger.info("Request recieved from client : " + question.toString());
        return new ResponseEntity<Question>(questionService.addQuestion(question),HttpStatus.OK);
    }
public Question addQuestion(Question question) {
   Question newQuestion = questionRepository.save(question);
   question.getQuestions().forEach(option -> {
      Option newOption = new Option();
      newOption.setQuestion(newQuestion); // need to reference managed (attached to JPA session) entity
      newOption.setOptionText(option.getOptionText());
      newOption.setCorrectAnswer(Optional.ofNullable(option.isCorrectAnswer()).orElse(false)));
      newQuestion.getOptions().add(optionRepository.save(newOption));
   });

   // it's done implicitly here because your controller's (or service's) method 
   // is marked with @Transactional, otherwise it must be done explicitly
   // newQuestion = questionRepository.save(newQuestion);

   return newQuestion;
}