Quarkus中Hibernate with Panache关联实体持久化外键为空问题解决
解决Quarkus Hibernate with Panache关联实体插入时外键为空问题
问题场景
在Quarkus项目中使用Hibernate with Panache,存在一对多关联的QuestionGroup和Question实体。执行批量插入QuestionGroup及其关联Question时,QuestionGroup可正常持久化,但Question报错Column 'question_group_id' cannot be null,要求在不分开持久化两类实体的前提下解决该问题。
实体类代码
QuestionGroup.java
@Entity @Table(name = "tb006_question_groups") public class QuestionGroup { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "question_group_id") @Getter @Setter private Long id; @Column(name = "question_group_code") @Getter @Setter private String code; @Column(name = "question_group_name") @Getter @Setter private String name; @CreationTimestamp @Temporal(TemporalType.TIMESTAMP) @Column(name = "question_group_createdAt") @Getter @Setter private Date createdAt; @UpdateTimestamp @Temporal(TemporalType.TIMESTAMP) @Column(name = "question_group_updatedAt") @Getter @Setter private Date updatedAt; @Column(name = "question_group_enabled") @Getter @Setter private Integer enabled; @OneToMany(mappedBy="questionGroup", cascade = CascadeType.PERSIST) @Getter @Setter private List<Question> questions; }
Question.java
@Entity @Table(name = "tb007_questions") public class Question { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "question_id") @Getter @Setter private Long id; @Column(name = "question_name") @Getter @Setter private String name; @Column(name = "question_answer") @Getter @Setter private String answer; @CreationTimestamp @Temporal(TemporalType.TIMESTAMP) @Column(name = "question_createdAt") @Getter @Setter private Date createdAt; @UpdateTimestamp @Temporal(TemporalType.TIMESTAMP) @Column(name = "question_updatedAt") @Getter @Setter private Date updatedAt; @Column(name = "question_enabled") @Getter @Setter private Integer enabled; @ManyToOne @JoinColumn(name="question_group_id", nullable=false) @Getter @Setter private QuestionGroup questionGroup; }
原插入方法
@Transactional @Override public QuestionGroup createQuestionGroup(QuestionGroupCreateRequestDTO questionGroupCreate) { QuestionGroup questionGroup = this.convertQuestionGroupCreateToQuestionGroup(questionGroupCreate); if (questionGroupCreate.getQuestions() != null) { List<Question> questions = questionGroupCreate.getQuestions().stream().map(question -> this.convertQuestionCreateToQuestion(question)).collect(Collectors.toList()); questionGroup.setQuestions(questions); } questionGroupRepository.persist(questionGroup); return questionGroup; }
解决方案
问题核心是双向关联未维护双方关系:JPA中双向关联的外键维护权在拥有@ManyToOne的一方(即Question),仅设置QuestionGroup的questions列表,未给每个Question绑定对应的questionGroup引用,导致Hibernate无法识别关联关系,插入Question时外键字段为空。
修改插入方法,在转换QuestionDTO为实体时,同步设置其questionGroup属性:
@Transactional @Override public QuestionGroup createQuestionGroup(QuestionGroupCreateRequestDTO questionGroupCreate) { QuestionGroup questionGroup = this.convertQuestionGroupCreateToQuestionGroup(questionGroupCreate); if (questionGroupCreate.getQuestions() != null) { List<Question> questions = questionGroupCreate.getQuestions().stream() .map(questionDTO -> { Question question = this.convertQuestionCreateToQuestion(questionDTO); // 维护双向关联:给Question绑定对应的QuestionGroup question.setQuestionGroup(questionGroup); return question; }) .collect(Collectors.toList()); questionGroup.setQuestions(questions); } questionGroupRepository.persist(questionGroup); return questionGroup; }
原理说明
尽管QuestionGroup的ID采用IDENTITY策略(persist后才生成),但Hibernate处理级联PERSIST时,会先保存QuestionGroup获取生成的ID,再自动将该ID填充到关联Question的question_group_id字段中——前提是两者的双向关联关系已正确建立。
内容的提问来源于stack exchange,提问作者nilsonjr
相关产品推荐
相关产品推荐

