[英]JPA unidirectional @OneToOne relationship with shared primary key always trigger a secondary query even if fetchType is EAGER
我正在構建一個博客系統,並希望為博客提供 upvote/downvote 功能。 由於博客的投票數應該被持久化,我選擇使用 MySQL 作為數據存儲。 我使用 Spring JPA(Hibernate) 來完成 ORM 工作。 這是我的數據對象:
class Blog{
// ...
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToOne(optional = false, fetch = FetchType.EAGER)
@PrimaryKeyJoinColumn
private BlogVoteCounter voteCounter;
}
和計數器 class:
@Entity
public class BlogVoteCounter extends ManuallyAssignIdEntitySuperClass<Long> {
@Id
private Long id;
private Integer value;
}
我將BlogVoteCounter
與Blog
分開的原因是,我認為與 Blog 的其他字段相比, voteCount
字段的修改頻率將完全不同,因為我想使用緩存來緩存Blog
,按照本指南,我選擇分開他們。
但是,由於在將博客 object 返回到前端時可能總是需要VoteCount
字段,並且為了避免 n+1 問題,我在博客 class 中聲明了BlogVoteCounter
字段為 EAGER 獲取類型。
我已經看過這篇文章了。 因此,根據我個人的理解,我使用單向關系,並且只在Blog
端聲明OneToOne
。
但是,當我檢查查詢時,事實證明 jpa 仍將觸發輔助查詢以從數據庫中檢索BlogVoteCounter
,而無需在BlogRepository
上使用findAll
方法時簡單地使用連接。
select
blogvoteco0_.id as id1_2_0_,
blogvoteco0_.value as value2_2_0_
from
blog_vote_counter blogvoteco0_
where
blogvoteco0_.id=?
那么我應該如何配置,始終使Blog
中的BlogVoteCounter
字段被熱切地獲取。
ManuallyAssignIdEntitySuperClass
的用法遵循 Spring JPA doc ,因為我手動為BlogVoteCounter
class 分配 id。
@MappedSuperclass
public abstract class ManuallyAssignIdEntitySuperClass<ID> implements Persistable<ID> {
@Transient
private boolean isNew = true;
@Override
public boolean isNew() {
return isNew;
}
@PrePersist
@PostLoad
void markNotNew(){
this.isNew = false;
}
}
而BlogRepository
是從JpaRepository
派生的
public interface BlogRepository extends JpaRepository<Blog, Long>{
// ...
}
我使用findAll
方法觸發查詢,但使用findById
或其他條件查詢似乎沒有區別。
When to fetch
與How to fetch
:fetchType
定義何時獲取關聯(later when someone access
instantly
與稍后)關聯,但不定義如何獲取關聯(即第二個 select 與加入查詢)。 因此,從 JPA 規范的角度來看,EAGER 意味着不要等到有人訪問該字段來填充它,但 JPA 提供者可以免費使用 JOIN 或第二個 Z99938282F04071859941E18F16EF,只要他們立即執行它。
盡管他們可以免費使用 join 與第二個 select,但我仍然認為他們應該針對 EAGER 的情況進行優化。 非常有興趣找出不使用連接的邏輯推理
1.為repository.findById(blogId);
select
blog0_.id as id1_0_0_,
blog0_.vote_counter_id as vote_cou2_0_0_,
blogvoteco1_.id as id1_1_1_,
blogvoteco1_.value as value2_1_1_
from
blog blog0_
inner join
blog_vote_counter blogvoteco1_
on blog0_.vote_counter_id=blogvoteco1_.id
where
blog0_.id=?
2.更新映射
public class Blog {
@Id
private Long id;
@ManyToOne(optional = false, cascade = ALL, fetch = FetchType.EAGER)
@PrimaryKeyJoinColumn
private BlogVoteCounter voteCounter;
public Blog() {
}
public Blog(Long id, BlogVoteCounter voteCounter) {
this.id = id;
this.voteCounter = voteCounter;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public BlogVoteCounter getVoteCounter() {
return voteCounter;
}
public void setVoteCounter(BlogVoteCounter voteCounter) {
this.voteCounter = voteCounter;
}
}
3. 當前映射的問題
blog
和votecounter
是不可能的,因為它會導致先有chicken and egg
的問題。 IE4.變化
5.注意事項
repository.findAll
正在生成 2 個查詢,因此我重寫了該方法以生成一個聯接查詢public interface BlogRepository extends JpaRepository<Blog, Long> {
@Override
@Query("SELECT b from Blog b join fetch b.voteCounter ")
List<Blog> findAll();
}
select
blog0_.id as id1_0_0_,
blogvoteco1_.id as id1_1_1_,
blog0_.vote_counter_id as vote_cou2_0_0_,
blogvoteco1_.value as value2_1_1_
from
blog blog0_
inner join
blog_vote_counter blogvoteco1_
on blog0_.vote_counter_id=blogvoteco1_.id
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.