[英]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.