繁体   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