0

我以为我理解了 JPA 的@JoinColumns注释和mappedBy参数,但后来我需要从这个 Json of a Question创建新实体。它有一组答案选择,也需要映射到新实体。我决定 Question 实体将成为拥有方,因此我省略了 mappedBy 参数。当我在AnswerChoice端使用 @JoinColumns 注释时,所有实体都是从 Json 对象创建的,但未设置 AnswerChoices 对 Question 实体的 FK。

将 @JoinColumns 放在 Question 实体中解决了问题,但我的问题是:这是正确的方法吗?我会面临任何副作用吗?我是否应该在 AnswerChoices 集上运行一个 for 循环并设置 FK?

问题Json

{
    "text": "Do you know JPA?",
    "answerChoices": [{
        "text": "yes",
    }, {
        "text": "no",
    }, ]
}

带有 JpaRepository 的控制器:

@PostMapping("/questions/create")
@ResponseBody
public String create(@RequestBody Question json) {
    questionRepo.save(json);
}

问题实体:

@Entity
public class Question {
    @OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY)
    @JoinColumn(name="question_id")
    private Set<AnswerChoice> answerChoices;
}

答案选择实体:

@Entity
public class AnswerChoice {
    @ManyToOne(fetch = FetchType.LAZY)
    @JsonIgnore
    private Question question;
}

为简洁起见,我省略了自动生成的 ID。

4

2 回答 2

3

不,映射不正确。它实际上创建了两个独立的关联,它们恰好共享连接列。

question从中删除Answer,使关联单向(问问自己是否真的需要关联的那一侧)或返回原始解决方案并使用@JsonBackReference/@JsonManagedReference(以便在反序列化期间自动填充该字段)。

于 2020-10-02T19:33:09.167 回答
3

正如@crizzis 所说,您的映射不正确,通常,孩子处于关系的拥有方(当一对多与单个域很大时),但在您的情况下,Question是拥有方既然你有关系@JoinColumn。所以你可以完全摆脱Question来自AnswerChoice. 当您使用答案选项创建问题时,休眠将

  • 创建问题
  • 创建答案
  • 更新回答问题的外键

如果你@JoinColumn(name = "question_id", foreignKey = @ForeignKey(name = "fk_question_id"))从 Question 实体中删除这一行,hibernate 将创建额外的表来管理这种关系,question_answer_choices因此为了摆脱额外的表,我们手动指定将引用哪个列AnswerChoice以映射到外键。

实体Question.java

@Entity
@Table(name = "question")
public class Question {
    @Id
    @GeneratedValue
    @Type(type = "uuid-char")
    private UUID id;

    private String description;

    @OneToMany(cascade = CascadeType.ALL)
    @JoinColumn(name = "question_id", foreignKey = @ForeignKey(name = "fk_question_id"))
    private Set<AnswerChoice> answerChoices = new HashSet<>();

    public UUID getId() {
        return id;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public void addAnswerChoice(AnswerChoice answerChoice) {
        if (answerChoice != null) {
            this.answerChoices.add(answerChoice);
        }
    }

    public Set<AnswerChoice> getAnswerChoices() {
        return answerChoices;
    }
}

实体AnswerChoice.java

@Entity
@Table(name = "answer_choice")
public class AnswerChoice {
    @Id
    @GeneratedValue
    @Type(type = "uuid-char")
    private UUID id;

    private String content;

    public AnswerChoice() {
    }

    public AnswerChoice(String content) {
        this.content = content;
    }


    public UUID getId() {
        return id;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }
}

下面的测试代码

    @Test
    public void testQuestionAndAnswersTest() {
        Question question = new Question();
        question.setDescription("How is the weather today?");

        question.addAnswerChoice(new AnswerChoice("Sunny"));
        question.addAnswerChoice(new AnswerChoice("Cloudy"));
        question.addAnswerChoice(new AnswerChoice("Rainy"));
        question.addAnswerChoice(new AnswerChoice("Windy"));
        question.addAnswerChoice(new AnswerChoice("Snowy"));

        //child entities persisted together
        entityManager.persist(question);


        Question searchedQuestion = entityManager.find(Question.class, question.getId());
        Assertions.assertNotNull(searchedQuestion);
        Assertions.assertNotNull(searchedQuestion.getId());
        Assertions.assertNotNull(searchedQuestion.getAnswerChoices());
        Assertions.assertEquals(5, searchedQuestion.getAnswerChoices().size());
        Set<AnswerChoice> answerChoices = searchedQuestion.getAnswerChoices();
        for (AnswerChoice answerChoice : answerChoices) {
            Assertions.assertNotNull(answerChoice.getId());
        }
    }

生成的表语句如下:

问题.sql

CREATE TABLE `question` (
  `id` varchar(255) NOT NULL,
  `description` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

answer_choice.sql

CREATE TABLE `answer_choice` (
  `id` varchar(255) NOT NULL,
  `content` varchar(255) DEFAULT NULL,
  `question_id` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `fk_question_id` (`question_id`),
  CONSTRAINT `fk_question_id` FOREIGN KEY (`question_id`) REFERENCES `question` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
于 2020-10-03T06:39:09.397 回答