Stackoverflowマッピング(Hibernate)
質問
このようなマッピングを想定
@Entity
public class User {
private Integer id
private List<Info> infoList;
@Id
public getId() {
return this.id;
}
@OneToMany(cascade=CascadeType.ALL)
@JoinColumn(name="USER_ID", insertable=false, updateable=false, nullable=false)
public getInfoList() {
return this.infoList;
}
public void addQuestion(Info question) {
info.setInfoCategory(InfoCategory.QUESTION);
info.setInfoId(new InfoId(getId(), getInfoList().size()));
getInfoList().add(question);
}
public void addAnswer(InfoRepository repository, Integer questionIndex, Info answer) {
Info question = repository.getInfoById(new InfoId(getId(), questionIndex));
if(question.getInfoCategory().equals(InfoCategory.ANSWER))
throw new RuntimeException("Is not a question");
if(question.getAnswer() != null)
throw new RuntimeException("You can not post a new answer");
answer.setInfoCategory(InfoCategory.ANSWER);
answer.setInfoId(new InfoId(getId(), getInfoList().size()));
getInfoList().add(answer);
question.setAnswer(answer);
}
}
そして、Infoクラスによってマップされた質問と回答
@Entity
public class Info implements Serializable {
private InfoId infoId;
private Info answer;
private InfoCategory infoCategory;
public Info() {}
@Embeddable
public static class InfoId {
private Integer userId;
private Integer index;
public InfoId(Integer userId, Integer index) {
this.userId = userId;
this.index = index;
}
@Column("USER_ID", updateable=false, nullable=false)
public getUserId() {
return this.userId;
}
@Column("INFO_INDEX", updateable=false, nullable=false)
public getIndex() {
return this.index;
}
// equals and hashcode
}
// mapped as a ManyToOne instead of @OneToOne
@ManyToOne
JoinColumns({
JoinColumn(name="USER_ID", referencedColumnName="USER_ID", insertable=false, updateable=false),
JoinColumn(name="ANSWER_INDEX", referencedColumnName="INFO_INDEX", insertable=false)
})
public Info getAnswer() {
return this.answer;
}
@EmbeddedId
public InfoId getInfoId() {
return this.infoId;
}
}
getAnswerでは、OneToOneマッピングに関連するいくつかの問題があるため、OneToOneではなくManyToOneを使用します。 OneToOneは、ManyToOneとしてマップできます(@JoinColumnで一意= true)。 INFO_INDEXは特定の目的とは関係ありません。 LEGACYシステムで複合主キーをサポートするための単なるキー。
回答する前に、次のことに注意してください。
オブジェクトに識別子または複合キーが割り当てられている場合、save()を呼び出す前に、オブジェクトインスタンスに識別子を割り当てる必要があります
そのため、Hibernateは2つの可変プロパティの共有を許可しないため、 JoinColumn(name =&quot; USER_ID&quot;、referencedColumnName =&quot; USER_ID&quot;、insertable = false、updateable = false)をgetAnswerにマッピングする必要があります同じ列(userIdもUSER_IDを使用します)そうでない場合、answerプロパティでUSER_IDを取得しますinsertable = false、updateable = falseでマップする必要があります
次に、getAnswerマッピングを見てみましょう
@ManyToOne
JoinColumns({
JoinColumn(name="USER_ID", referencedColumnName="USER_ID", insertable=false, updateable=false),
JoinColumn(name="ANSWER_INDEX", referencedColumnName="INFO_INDEX", insertable=false)
})
それが原因で、Hibernateは異なる挿入可能と更新可能を混在させることはできないと文句を言います
合格するにはどうすればよいですか?
LEGACYシステムであることに注意してください。
よろしく、
解決 2
getAnswerマッピングの質問によると
@ManyToOne
JoinColumns({
JoinColumn(name="USER_ID", referencedColumnName="USER_ID", insertable=false, updateable=false),
JoinColumn(name="ANSWER_INDEX", referencedColumnName="INFO_INDEX", insertable=false)
})
異なる挿入可能および更新可能を混在させることができないため、Hibernateは文句を言います。 USER_ID JoinColumnで挿入および更新可能で、ANSWER_INDEX JoinColumnでのみ挿入可能であることに注意してください。
それを渡すために、ANSWER_INDEX JoinCollumnを設定しました
JoinColumn(name="ANSWER_INDEX", referencedColumnName="INFO_INDEX", insertable=false, updateable=false)
この方法では、Hibernateは文句を言いません。
そしてanswerIndexという新しいプロパティを設定します
private Integer answerIndex;
@Column(name="ANSWER_INDEX", insertable=false)
public void getAnswerIndex() {
return this.answerIndex;
}
次に、ユーザーaddAnswerで
public void addAnswer(InfoRepository repository, Integer questionIndex, Info answer) {
Info question = repository.getInfoById(new InfoId(getId(), questionIndex));
if(question.getInfoCategory().equals(InfoCategory.ANSWER))
throw new RuntimeException("Is not a question");
if(question.getAnswer() != null)
throw new RuntimeException("You can not post a new answer");
answer.setInfoCategory(InfoCategory.ANSWER);
answer.setInfoId(new InfoId(getId(), getInfoList().size()));
getInfoList().add(answer);
// Added in order to set up AnswerIndex property
question.setAnswerIndex(answer.getInfoId().getIndex());
}
他のヒント
埋め込まれたIDを放棄し、代わりにサロゲートPKを使用することにより、マッピングを劇的に単純化します。
何らかの目的で InfoId.index
を使用する必要がある場合(質問/回答を注文するには、 list
マッピングで指定できます)、それを通常のプロパティとして保持します。
User
で UserId
は ManyToOne
に置き換えられます。 ( User
クラスの)他の関連付けの終わりは、 @OneToMany(mappedBy =&quot; User&quot;)
なぜ回答が ManyToOne
としてマッピングされるのですか? OneToMany
(たとえば、1つの質問から多くの回答まで)であってはなりませんか?いずれにしても、クラスをそれ自体にマッピングすることは理想的ではありません。基本的には「隣接リスト」を実装しています。非効率的なモデル階層。さらに、あなたの場合、それが2レベル以下であることを確認する必要があります。 Question
と Answer
を別々のクラスとしてマッピングします。共通のインターフェースを実装するか、同じ基本(場合によっては抽象)クラスを拡張することができます。どちらの方法でも、Hibernateが提供する暗黙的なポリモーフィズムを楽しむことができます。