Java 将外键作为复合主键的一部分级联保存实体对象

Java 将外键作为复合主键的一部分级联保存实体对象,java,spring,hibernate,jpa,spring-mvc,Java,Spring,Hibernate,Jpa,Spring Mvc,我想用所有子元素持久化我的QuestionCompletion类的对象。其中一个孩子有一个复合主键。作为主键的一部分,我还有另一个实体的外键。因此,我得到以下错误: Exception caught during request processing: javax.ejb.EJBTransactionRolledbackException: could not set a field value by reflection setter of com.example.model.domain.Q

我想用所有子元素持久化我的
QuestionCompletion
类的对象。其中一个孩子有一个复合主键。作为主键的一部分,我还有另一个实体的外键。因此,我得到以下错误:

Exception caught during request processing: javax.ejb.EJBTransactionRolledbackException:
could not set a field value by reflection setter of com.example.model.domain.QuestionCompletionAnswerPK.questionCompletionId
javax.ejb.EJBTransactionRolledbackException: could not set a field value by reflection
setter of com.example.model.domain.QuestionCompletionAnswerPK.questionCompletionId
最后一个“原因”当然是
NullPointerException

Caused by: java.lang.NullPointerException
这是我代码的一部分。最后一行导致错误

QuestionCompletion questionCompletion = new QuestionCompletion();
List<QuestionCompletionAnswer> answers = new ArrayList<QuestionCompletionAnswer>();
for (;;) { // loop isn't important; it's loop for answers
    ExtendedQuestion extendedQuestion = new ExtendedQuestion();
    extendedQuestion.setId(extendedQuestionId); //extendedQuestionId is known to me in that place
    for (;;) { // loop isn't important; it's loop for question answers
        //questionCompletion and extendedQuestion are popualted here
        QuestionCompletionAnswer questionCompletionAnswer = new QuestionCompletionAnswer();
        questionCompletionAnswer.setQuestionCompletion(questionCompletion); 
        questionCompletionAnswer.setExtendedQuestion(extendedQuestion); 
        answers.add(questionCompletionAnswer);
    }
}
questionCompletion.setAnswers(answers);
questionCompletionService.saveOrMerge(questionCompletion);
这是我的类-问题完成答案类的主键

@Embeddable
public class QuestionCompletionAnswerPK implements Serializable {

    @Column(name = "question_completion_id")
    protected Long questionCompletionId;

    @Column(name = "extended_question_id")
    protected Long extendedQuestionId;
}
这是一个使用my
EmbeddedId
的类。属性
questionCompletionId
questionCompletionId
是其他一些实体的外键,因此我在下面也放置了这些实体的完整对象,并带有
@MapsId
注释

@javax.persistence.Entity
@org.hibernate.annotations.Entity(dynamicUpdate = true)
@Table(name = "extended_question_answer")
public class QuestionCompletionAnswer implements Serializable {

    @EmbeddedId
    private QuestionCompletionAnswerPK id;

    @ManyToOne
    @MapsId(value = "questionCompletionId")
    @JoinColumn(name = "question_completion_id", nullable = false, insertable = false, updatable = false)
    protected QuestionCompletion questionCompletion;

    @ManyToOne
    @MapsId(value = "extendedQuestionId")
    @JoinColumn(name = "extended_question_id", nullable = false, insertable = false, updatable = false)
    protected ExtendedQuestion extendedQuestion;
}
你能告诉我我的注解是否正确吗?也许我混淆了一些方法。或者,在这种情况下,我无法将基本对象及其所有子元素持久化

编辑

现在,我的映射看起来像:

@javax.persistence.Entity
@org.hibernate.annotations.Entity(dynamicUpdate = true)
@Table(name = "question_completion")
public class QuestionCompletion implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "question_completion_gen")
    @SequenceGenerator(name = "question_completion_gen", sequenceName = "question_completion_id_seq")
    @Column(name = "question_completion_id")
    private Long id;

    @OneToMany(cascade = CascadeType.ALL, mappedBy = "questionCompletion")
    protected List<QuestionCompletionAnswer> answers;
}
使用该映射,我仍然会得到相同的异常

编辑#2 但是,当我以这种方式更改
QuestionCompletionAnswer
类时:

@javax.persistence.Entity
@org.hibernate.annotations.Entity(dynamicUpdate = true)
@Table(name = "extended_question_answer")
public class QuestionCompletionAnswer implements Serializable {

    @EmbeddedId
    private QuestionCompletionAnswerPK id;

    @ManyToOne(cascade = CascadeType.PERSIST)
    @JoinColumn(name = "question_completion_id", nullable = false, insertable = false, updatable = false)
    protected QuestionCompletion questionCompletion;

    @ManyToOne
    @JoinColumn(name = "extended_question_id", nullable = false, insertable = false, updatable = false)
    protected ExtendedQuestion extendedQuestion;
}
我得到了一个例外:

Caused by: org.hibernate.id.IdentifierGenerationException: null id generated 
for:class com.example.model.domain.QuestionCompletionAnswer

编辑1和编辑2仍然不正确。您需要在关系上指定mapsid,或者您必须自己在嵌入的id中设置一个值。当您使用mapsid时,不应该将连接列标记为insertable=false,或者jpa不能为您插入值。我看到的最后一个问题是,新问题没有被持久化,因此它没有得到一个分配给答案可以引用的id-您需要在for循环中显式地持久化新问题,或者在答案中标记关系以级联持久化。

为什么混合使用jpa和hibernate注释?仅jpa注释就足够了。 尝试以下映射(我删除了类
QuestionCompletionAnswerPK
):

QuestionCompletion.java

@Entity
@Table(name = "question_completion")
public class QuestionCompletion {

    @Id
    @SequenceGenerator(name = "question_completion_gen", sequenceName = "question_completion_id_seq")
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "question_completion_gen")
    @Column(name = "question_completion_id")
    private Long id;

    @OneToMany(cascade = CascadeType.ALL, mappedBy = "questionCompletion")
    protected List<QuestionCompletionAnswer> answers;
}
@Entity
@Table(name = "extended_question_answer")
public class QuestionCompletionAnswer implements Serializable {

    @Id
    @ManyToOne
    @JoinColumn(name = "question_completion_fk")
    private QuestionCompletion questionCompletion;

    @Id
    @ManyToOne
    @JoinColumn(name = "extended_question_fk")
    private ExtendedQuestion extendedQuestion;
}
有了这个映射,我运行了以下测试用例,它成功了:

QuestionCompletion questionCompletion = new QuestionCompletion();
ExtendedQuestion extendedQuestion = new ExtendedQuestion();
QuestionCompletionAnswer answer = new QuestionCompletionAnswer();
answer.setQuestionCompletion(questionCompletion);
answer.setExtendedQuestion(extendedQuestion);
questionCompletion.setAnswers(Collections.singletonList(answer));
...
session.save(extendedQuestion);
session.save(questionCompletion);

不要将@joincolumn添加到onetomany映射。改为使用extendedQuestion映射将其标记为mappedby。同时删除多通joincolumn定义中的updateable=false insertable=false,因为mapsid注释暗示了这些属性。@Chris感谢您的评论。我已经更改了您描述的那几行,但是我仍然得到相同的错误。您应该更改上面的代码,以显示您现在拥有的代码,因为上面的模型是错误的。还显示NPE堆栈以查看它的来源,并确保您正在持久化/合并引用的问题,以便为这些问题分配ID。您可能需要在questionCompletion映射上设置cascade persist。我的错误正好出现在保存时
questionCompletion
-
Serializable id=getSession()。保存(questionCompletion)
所以事实上我没有得到这个实体的id。@Chris我已经更新了我的问题。我仍然得到相同的异常。我已经实现了您描述的更改。但是保存
问题完成回答时仍然存在问题-相同的例外。对我来说,
cascade=CascadeType。在这种情况下,所有的
都不起作用。我将明确地保存它。但是,如果没有
QuestionCompletionAnswer
对象的列表,
QuestionCompletion
保存时不会出现任何问题。错误表示NPE正在尝试在QuestionCompletionAnswerPK中设置字段。您的QuestionCompletionAnswerPK是否为id分配了QuestionCompletionAnswerPK?如果在创建新问题CompletionAnswerPK时分配新问题CompletionAnswerPK没有帮助,则可能发布完整的NPE堆栈跟踪。
@Entity
@Table(name = "extended_question_answer")
public class QuestionCompletionAnswer implements Serializable {

    @Id
    @ManyToOne
    @JoinColumn(name = "question_completion_fk")
    private QuestionCompletion questionCompletion;

    @Id
    @ManyToOne
    @JoinColumn(name = "extended_question_fk")
    private ExtendedQuestion extendedQuestion;
}
QuestionCompletion questionCompletion = new QuestionCompletion();
ExtendedQuestion extendedQuestion = new ExtendedQuestion();
QuestionCompletionAnswer answer = new QuestionCompletionAnswer();
answer.setQuestionCompletion(questionCompletion);
answer.setExtendedQuestion(extendedQuestion);
questionCompletion.setAnswers(Collections.singletonList(answer));
...
session.save(extendedQuestion);
session.save(questionCompletion);