Я создаю веб-сайт блога, и есть два типа объектов, за которые можно проголосовать прямо сейчас: блог и комментарий. Я хочу использовать одну таблицу для хранения этих двух типов счетчиков голосов. Я думаю, что пара (entity_id, type)
может быть составным первичным ключом, вот мой базовый класс Java @Entity:
@Entity
@Table(name = "vote_counter")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorOptions(insert = false)
@DiscriminatorColumn(name = "type", discriminatorType = DiscriminatorType.INTEGER)
public abstract class VoteCounter extends ManuallyAssignIdEntitySuperClass<VoteCounterId> {
@EmbeddedId
private VoteCounterId id;
@Column(nullable = false)
private Integer voteCount = 0;
public VoteCounterId getId() {
return id;
}
public void setId(VoteCounterId id) {
this.id = id;
}
public Integer getVoteCount() {
return voteCount;
}
public void setVoteCount(Integer voteCount) {
this.voteCount = voteCount;
}
public void incrVoteCount(int value){
voteCount += value;
}
public void decrVoteCount(int value){
voteCount -= value;
}
public VoteCounter() {
}
public VoteCounter(VoteCounterId id) {
this.id = id;
}
public VoteCounter(long entityId, int type){
this.id = new VoteCounterId(entityId, type);
}
}
Я следую руководствам post и создаю ManuallyAssignIdEntitySuperClass
. А вот определение класса @EmbeddedId
:
@Embeddable
public class VoteCounterId implements Serializable {
@Column(name = "entity_id", nullable = false, updatable = false)
private Long entityId;
@Column(name = "type", nullable = false, insertable = false, updatable = false)
private Integer type;
public VoteCounterId() {
}
public VoteCounterId(Long entityId, Integer type) {
this.entityId = entityId;
this.type = type;
}
public Long getEntityId() {
return entityId;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof VoteCounterId)) return false;
VoteCounterId that = (VoteCounterId) o;
return Objects.equals(entityId, that.entityId) &&
Objects.equals(type, that.type);
}
@Override
public int hashCode() {
return Objects.hash(entityId, type);
}
}
Класс basi c VoteCounter
имеет два подкласса, BlogVoteCounter
и CommentVoteCounter
.
@Entity
@DiscriminatorValue(VoteType.COMMENT_DISCRIMINATOR)
public class CommentVoteCounter extends VoteCounter {
@OneToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "entity_id", updatable = false, nullable = false, insertable = false)
private Comment comment;
public CommentVoteCounter() {
}
public CommentVoteCounter(long commentId) {
super(commentId, VoteType.COMMENT);
}
public Comment getComment() {
return comment;
}
}
I ' Мы создадим метод для проверки поведения этих классов. Как видите, семанти c здесь похоже на «проверьте, существует ли blogVoteCounter
или нет, если существует, увеличьте его количество голосов, если нет, просто создайте его».
public BlogVoteCounter incrBlogVoteCounter(long id){
return blogVoteCounterRepository.findByBlog(blogRepository.getOne(id)).map(blogVoteCounter -> {
blogVoteCounter.incrVoteCount(1);
return blogVoteCounterRepository.save(blogVoteCounter);
}).orElseGet(() -> {
BlogVoteCounter blogVoteCounter = new BlogVoteCounter(id);
return blogVoteCounterRepository.save(blogVoteCounter);
});
}
Теперь вот проблема: после создания начального BlogVoteCounter
в базе данных для определенного c Blog
, каждый раз, когда я выполняю метод для этого Blog
, кажется, что hibernate / jpa всегда будет запускать дополнительный запрос выбора до save
. точно так же:
Hibernate:
select
blogvoteco0_.entity_id as entity_i2_8_,
blogvoteco0_.type as type1_8_,
blogvoteco0_.vote_count as vote_cou3_8_
from
vote_counter blogvoteco0_
where
blogvoteco0_.type=1
and blogvoteco0_.entity_id=?
Hibernate:
select
blogvoteco0_.entity_id as entity_i2_8_0_,
blogvoteco0_.type as type1_8_0_,
blogvoteco0_.vote_count as vote_cou3_8_0_
from
vote_counter blogvoteco0_
where
blogvoteco0_.entity_id=?
and blogvoteco0_.type=?
and blogvoteco0_.type=1
Hibernate:
update
vote_counter
set
vote_count=?
where
entity_id=?
and type=?
Но если я добавлю аннотацию @Transactional
к методу incrBlogVoteCounter
, дополнительный запрос выбора перед обновлением не появится. Я предполагаю, что BlogVoteCounter
, полученный методом findByBlog
, отсоединен, поэтому запрос выбора будет выполнен перед обновлением, но мне просто интересно, почему он будет отсоединен (если я предполагаю, что это запись). Я использую тот же шаблон (поиск и обновление) в другом месте моего приложения, и все они не вызовут эту проблему. Я хочу знать, как избежать этого лишнего запроса?