Сопоставление 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 я использую ManyToOne вместо OneToOne, поскольку некоторые проблемы связаны с сопоставлением OneToOne.OneToOne можно сопоставить как ManyToOne (unique=true в @JoinColumn).INFO_INDEX не имеет отношения к какой-либо конкретной цели.Просто ключ для поддержки составного первичного ключа в СТАРОЙ системе.
Прежде чем ответить, обратите внимание на следующее:
Если объекту присвоен идентификатор или составной ключ, идентификатор ДОЛЖЕН БЫТЬ НАЗНАЧЕН экземпляру объекта ПЕРЕД вызовом метода save().
Поэтому мне нужно составить карту JoinColumn(name="USER_ID", referencedColumnName="USER_ID", Insertable=false, updateable=false) в getAnswer, потому что Hibernate не позволяет двум изменяемым свойствам использовать один и тот же столбец (userId также использует USER_ID), в противном случае я получу 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 жалуется, что нельзя смешивать разные вставляемые и обновляемые файлы.
Что мне нужно сделать, чтобы пройти его?
Будьте осторожны, это УСТАРЕВШАЯ система.
с уважением,
Решение 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;
}
Затем в 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());
}
Другие советы
Ты бы кардинально упростите сопоставление, отказавшись от встроенного идентификатора и вместо этого используя суррогатный PK.
Если вам нужно использовать InfoId.index
с какой-то целью (заказать вопросы/ответы?вы можете указать это в list
сопоставление), сохраните его как обычное свойство.
UserId
будет заменен на ManyToOne
на User
;другой конец ассоциации (в User
class) будет отображаться как @OneToMany(mappedBy="User")
Почему ответ отображается как ManyToOne
?Разве это не должно быть OneToMany
(например.1 вопрос на множество ответов)?В любом случае, сопоставление класса с самим собой не идеально - вы по сути реализуете иерархию модели «списка смежности», которая неэффективна;плюс в вашем случае вам нужно будет убедиться, что это не более 2 уровней.я бы нарисовал карту Question
и Answer
как отдельные классы;вы можете заставить их реализовать общий интерфейс или расширить один и тот же базовый (возможно, абстрактный) класс;в любом случае вы сможете пользоваться неявным полиморфизмом, предоставляемым Hibernate.