Java 从Json正确创建的实体,反面带有@JoinColumn

Java 从Json正确创建的实体,反面带有@JoinColumn,java,jpa,spring-data-jpa,joincolumn,Java,Jpa,Spring Data Jpa,Joincolumn,我以为我理解了JPA的@JoinColumns注释和mappedBy参数,但后来我需要从这个Json问题中创建新的实体。它有一组答案选择,也需要映射到新的实体。我决定问题实体将是拥有方,因此我省略了mappedBy参数。当我在AnswerChoice端使用@JoinColumns注释时,所有实体都是从Json对象创建的,但是AnswerChoices的FK到问题实体没有设置 将@JoinColumns放在问题实体中解决了问题,但我的问题是:这是正确的方法吗?我会面临任何副作用吗?我应该在应答器选

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

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

问题Json

{
    "text": "Do you know JPA?",
    "answerChoices": [{
        "text": "yes",
    }, {
        "text": "no",
    }, ]
}
具有JPA存储库的控制器:

@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;
}

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

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


要么从答案中删除问题,使关联单向问自己是否真的需要关联的那一方,要么返回原始解决方案并使用,以便在反序列化过程中自动填充字段。

正如@crizzis所说,您的映射通常是不正确的,当一个域的1对多比较大时,子对象处于关系的拥有方,但在您的情况下,问题是关系的拥有方,因为您有@JoinColumn。这样你就可以完全摆脱答案选择中的问题参考。当您创建带有答案选项的问题时,hibernate将

提出问题 创造答案 更新外键问题的答案 如果从问题实体中删除这一行@JoinColumnname=question\u id,foreignKey=@ForeignKeyname=fk\u question\u id,hibernate将创建额外的表来管理名为question\u answer\u choices的关系,因此为了摆脱额外的表,我们手动指定AnswerChoice中的哪一列将被引用以映射到外键

实体问题.java

@实体 @Tablename=问题 公开课问题{ @身份证 @生成值 @Typetype=uuid字符 私有UUID; 私有字符串描述; @OneToManycascade=CascadeType.ALL @JoinColumnname=question\u id,foreignKey=@ForeignKeyname=fk\u question\u id 私有集answerChoices=新哈希集; 公共UUID getId{ 返回id; } 公共字符串getDescription{ 返回说明; } public void setDescription字符串描述{ this.description=描述; } public void addanswerchoice answerChoice answerChoice answerChoice{ 如果answerChoice!=null{ this.answerChoices.addanswerChoice; } } 公共集getAnswerChoices{ 回答选择; } } 实体AnswerChoice.java

@实体 @Tablename=答案\选项 公共类答案选择{ @身份证 @生成值 @Typetype=uuid字符 私有UUID; 私有字符串内容; 公共答案选择{ } 公共回答选择字符串内容{ this.content=内容; } 公共UUID getId{ 返回id; } 公共字符串getContent{ 返回内容; } 公共void setContentString内容{ this.content=内容; } } 测试代码如下

@试验 公开无效测试问题和答案测试{ 问题=新问题; 问:今天天气怎么样?; 问题。添加答案选项新建答案选项Runny; 问题。添加答案选项新建答案选项音量; 问题。添加答案选项新答案选项; 问题。添加答案选项新答案选项Windy; 问题。添加答案选项新建答案选项Nowy; //子实体保持在一起 实体管理者问题; Question searchedQuestion=entityManager.findQuestion.class,Question.getId; assertNotNullsearchedQuestion; assertNotNullsearchedQuestion.getId; assertNotNullsearchedQuestion.getAnswerChoices; assertEquals5,searchedQuestion.getAnswerChoices.size; 设置answerChoices=searchedQuestion.getAnswerChoices; 回答选择:回答选择{ 断言.assertNotNullanswerChoice.getId; } } 生成的表语句如下所示:

questions.sql

创建表“问题” `id`varchar255不为空, `description`varchar255默认为空, 主键'id` ENGINE=InnoDB默认字符集=utf8mb4 COLLATE=utf8mb4\U 0900\U ai\U 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

谢谢你的回答。如果两个关联都跟踪两侧的更改并生成正确的SQL,那么为什么两个关联共享同一列是不好的?另外,你能详细说明一下w吗
从技术上讲,JPA中的关联是什么?内存中的某些数据结构?让双方都假装是关联的所有者是不好的,因为这样的配置行为是不可预测的,例如,如果您将Answer.question设置为null,但将答案保留在question.answerChoices中,最终的结果是,任何人的猜测关联在面向对象的意义上仅仅意味着“一个实体引用另一个实体”,而不是JPA特有的含义,并且有特定的方法告诉JPA如何处理它们。当两个关联之间存在逻辑链接时,您可以告诉JPA,这两个关联是同一双向关联的一部分,在其中一侧使用mappedBy告诉JPA:“此关联的DB映射在另一个实体中定义”。您的映射根本不允许JPA识别您的是双向关联。它有时可能会起作用,但在其他情况下可能会失败
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