假设像这样的映射
@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中,我使用ManyToOne而不是OneToOne,因为一些问题与OneToOne映射有关。 OneToOne可以映射为ManyToOne(@JoinColumn中的unique = true)。 INFO_INDEX与任何特定目的无关。只是一个键,以便在LEGACY系统中支持复合主键。
在回答之前,请注意以下事项:
如果对象具有指定的标识符或复合键,则在调用save()
之前,应该将标识符分配给对象实例。
所以我必须在getAnswer中映射 JoinColumn(name =“USER_ID”,referencedColumnName =“USER_ID”,insertable = false,updateable = false),因为Hibernate不允许两个可变属性共享相同的内容collumn(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抱怨你不能混合不同的可插入和可更新的
我该怎么办才能通过它?
小心这是一个遗产系统。
的问候,
答案 0 :(得分:2)
您将彻底通过放弃嵌入式ID并使用代理PK来简化您的映射。
如果您出于某种目的需要使用InfoId.index
(订购问题/答案?您可以在list
映射中指定),请将其保留为常规属性。
UserId
将替换为ManyToOne
上的User
;另一个关联结束(在User
类中)将被映射为@OneToMany(mappedBy="User")
为什么答案会映射为ManyToOne
?不应该是OneToMany
(例如1个问题到多个答案)?无论哪种方式,将类映射到自身都不太理想 - 您基本上实现了一个低效的“邻接列表”模型层次结构;在你的情况下,你需要确保它不超过2个级别。我将Question
和Answer
映射为单独的类;你可以让他们实现一个通用的接口或扩展相同的基础(可能是抽象的)类;无论哪种方式,你都可以享受Hibernate提供的隐式多态性。
答案 1 :(得分:1)
根据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;
}
然后在User 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());
}