Я пытаюсь создать демонстрационное приложение вопросов и ответов, используя Spring Boot, Hibernate и PostgreSQL.У меня есть класс Question
, который имеет @ElementCollection
из Vote
с.Вот определение Vote.java
:
@MappedSuperclass
public abstract class Vote implements Serializable {
protected enum VoteDirection {
UP("UP"), DOWN("DOWN");
private String text;
VoteDirection(String text) {
this.text = text;
}
public String toString() {
return text;
}
}
public static Vote fromString(String userID, String voteDirection) {
if (voteDirection.equals(VoteDirection.UP.toString())) {
return upvote(UUID.fromString(userID));
} else if (voteDirection.equals(VoteDirection.DOWN.toString())) {
return downvote(UUID.fromString(userID));
} else {
throw new IllegalArgumentException("Unsupported String format: " + voteDirection);
}
}
public static Vote upvote(UUID userID) {
return new Upvote(userID);
}
public static Vote downvote(UUID userID) {
return new Downvote(userID);
}
@Type(type = "uuid-char")
@Column(name = "userid", nullable = false)
private UUID userID;
public Vote() {
}
public Vote(UUID userID) {
this.userID = userID;
}
@Enumerated(value = EnumType.STRING)
@Column(name = "type")
protected VoteDirection type;
public UUID getUserID() {
return userID;
}
public String getType() {
return type.toString();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
Vote vote = (Vote) o;
return getUserID().toString().equals(vote.getUserID().toString());
}
@Override
public int hashCode() {
return getUserID().toString().hashCode();
}
public abstract Integer getScore();
}
Upvote
и Downvote
являются конкретными подклассами Vote
.Вот определение Question
:
@Entity
@Table(name = "questions")
@org.hibernate.annotations.TypeDef(name = "vote_type", typeClass = VoteType.class)
public class Question extends AuditModel {
@Id
@GeneratedValue(generator = "UUID")
@org.hibernate.annotations.GenericGenerator(
name = "UUID",
strategy = "org.hibernate.id.UUIDGenerator"
)
private UUID id;
@NotBlank
@Size(min = 3, max = 100)
private String title;
@Column(columnDefinition = "text")
private String description;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "question")
@JsonIgnore
private Set<Answer> answers = new HashSet<>();
@org.hibernate.annotations.Type(type = "vote_type")
@org.hibernate.annotations.Columns(columns = {
@Column(name = "userid", nullable = false),
@Column(name = "type", nullable = false)
})
@ElementCollection(fetch = FetchType.LAZY)
private Set<Vote> votes = new HashSet<>();
public Set<Answer> getAnswers() {
return answers;
}
public UUID getId() {
return id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Set<Vote> getVotes() {
return Collections.unmodifiableSet(votes);
}
public void upvote(UUID userID) {
votes.add(new Upvote(userID));
}
public void downvote(UUID userID) {
votes.add(Vote.downvote(userID));
}
public Integer getScore() {
return votes.stream().mapToInt(Vote::getScore).sum();
}
}
Чтобы поддержать полиморфизм @ElementCollection
, я создал CompositeUserType
с именем VoteType
, и полиморфизм работает, протестированный с использованием метода getScore()
.
Однако всякий раз, когда я добавляю новый голос в коллекцию votes
, Hibernate удаляет, а затем заново вставляет все голоса по одному.Поиски в Google и Java Persistence with Hibernate
предполагают, что переопределение hashCode()
и equals()
должно решить эту проблему, а также указание столбцов как необнуляемых, чтобы они были включены в ограничение первичного ключа.Но я сделал обе эти вещи, и все же проблема все еще остается.Я протестировал методы equals()
и hashCode()
, чтобы проверить, действительно ли они работают правильно, что они и делают.Что еще может быть причиной такого поведения?
Edit
То же самое происходит, когда я использую сумку, Collection<>
с реализацией ArrayList<>
.Это заставляет меня поверить, что Hibernate видит коллекцию как-то отредактированной.