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

时间:2013-03-06 18:53:27

标签: 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.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);

这是我的基本实体类,我想坚持其所有的子元素。我意识到List<QuestionCompletionAnswer>会导致问题。我已经使用cascade = CascadeType.ALL来允许持久化子元素。

@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)
    @JoinColumn(name = "extended_question_id")
    protected List<QuestionCompletionAnswer> answers;
}

这是我的班级 - QuestionCompletionAnswer班级的主键。

@Embeddable
public class QuestionCompletionAnswerPK implements Serializable {

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

    @Column(name = "extended_question_id")
    protected Long extendedQuestionId;
}

这是使用我EmbeddedId的课程。归因questionCompletionIdquestionCompletionId是其他实体的外键,因此我在下面还将这些实体的整个对象放在@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;
}

QuestionCompletionAnswerPK类的代码是相同的。

@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)
    @MapsId(value = "questionCompletionId")
    @JoinColumn(name = "question_completion_id", nullable = false)
    protected QuestionCompletion questionCompletion;

    @ManyToOne
    @MapsId(value = "extendedQuestionId")
    @JoinColumn(name = "extended_question_id", nullable = false)
    protected ExtendedQuestion extendedQuestion;
}

通过该映射,我仍然得到同样的例外。

编辑#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

2 个答案:

答案 0 :(得分:3)

编辑1和2仍然不对。您需要在关系中指定的mapsid,或者您必须自己在嵌入式ID中设置值。当您使用mapsid时,您不应该将连接列标记为insertable = false或jpa无法为您插入值。我看到的最后一个问题是新问题没有持久化,所以它没有得到答案可以引用的id - 你需要在for循环中明确地保留新问题或者在答案中标记关系到级联持续存在。

答案 1 :(得分:1)

为什么要混用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;
}

<强> QuestionCompletionAnswer.java

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