gpt4 book ai didi

java - 为什么 jpa/hibernate 在保存从 "select-then-update"方法检索的实体时仍然执行 "findBy",似乎它已分离

转载 作者:行者123 更新时间:2023-12-01 16:15:27 30 4
gpt4 key购买 nike

我正在构建一个博客网站,现在可以投票的实体有两种类型:博客和评论。我想用一张表来存储这两种类型的投票计数器。我认为 (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);
}
}

基本的VoteCounter类有两个子类,BlogVoteCounterCommentVoteCounter

@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;
}
}

我创建了一个方法来测试这些类的行为。正如你所看到的,这里的语义就像“检查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);
});
}

现在问题来了,在数据库中为特定的 Blog 创建初始 BlogVoteCounter 后,每次我在该 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=?

但是如果我在 incrBlogVoteCounter 方法上添加 @Transactional 注释,则更新前的额外选择查询将不会出现。我猜测由 findByBlog 方法检索的 BlogVoteCounter 已分离,因此选择查询将在更新之前执行,但我只是想知道为什么它会被分离(如果我的猜测是写)。我在应用程序的其他地方使用相同的模式(查找和更新),并且所有这些都不会出现此问题。我想知道如何避免这个额外的查询?

最佳答案

but i just wonder why it will me detached

正是因为该方法没有包装在事务中。在这种情况下,只有findByBlog创建自己的事务,一旦方法调用完成,持久化上下文就会被清除,并且所有以前管理的实体都会被分离。

如果您想知道为什么要执行额外的选择,那是因为 blogVoteCounterRepository.save(...)调用EntityManager.merge()内部。 EntityManager.merge()需要将实体的现有版本加载到持久化上下文中,以便能够执行合并(因此需要额外的 SELECT )。当您使用@Transactional时不过,该实体已经在上下文中,因此无需加载任何内容(事实上, EntityManager.merge() 没有任何效果)。

关于java - 为什么 jpa/hibernate 在保存从 "select-then-update"方法检索的实体时仍然执行 "findBy",似乎它已分离,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62405061/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com