簡體   English   中英

JPA 與共享主鍵的單向@OneToOne 關系始終觸發輔助查詢,即使 fetchType 為 EAGER

[英]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;
}

我將BlogVoteCounterBlog分開的原因是,我認為與 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 fetchHow to fetchfetchType定義何時獲取關聯( 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. 當前映射的問題

  • 根據您的映射,創建blogvotecounter是不可能的,因為它會導致先有chicken and egg的問題。 IE
  • blog 和 votecounter 需要共享同一個主鍵
  • 博客的主鍵是由數據庫生成的。
  • 所以為了獲取博客的主鍵並將其分配給投票計數器,您需要先存儲博客
  • 但是@OneToOne 關系不是可選的,所以你不能先單獨存儲博客

4.變化

  • 要么需要使關系可選,因此可以先存儲博客,獲取 id,分配給 BlogVoteCounter 並保存計數器
  • 或者不要自動生成 ID 並手動分配 ID,這樣可以同時保存博客和投票計數器。(我已經選擇了這個選項,但你可以做第一個選項)

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM