首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >这是一个N+1问题吗?我如何解决它?

这是一个N+1问题吗?我如何解决它?
EN

Stack Overflow用户
提问于 2016-04-12 09:36:40
回答 3查看 6K关注 0票数 0

我有一个项目清单和一个客户名单。一个项目可以是为一个客户,每个客户可以有许多项目。因此,这是一个简单的1:n关系,其中项目是拥有的一方。

简化为本质

代码语言:javascript
运行
复制
@Entity
public class Project {
  @Id
  long id;

  @ManyToOne(optional = true)
  @JoinColumn(name = "customer", nullable = true, updatable = true)
  Customer customer;
}

@Entity
public class Customer {
  @Id
  long id;
}

当我加载项目列表时,我希望同时高效地检索客户。事实并非如此。对于项目只有一个查询,然后针对遇到的每个不同的客户发出单独的查询。

因此,假设我有100个项目分配给50个不同的客户。这将导致对项目的一个查询和对客户的50个查询。

这很快就会增加,对于大型项目/客户列表,我们的应用程序变得相当缓慢。这也只是一个例子。我们所有有关系的实体都会受到这种行为的影响。

我已经像建议的那样在@Fetch(FetchMode.JOIN)字段上尝试了here,但是它什么也不做,根据Hibernate,FetchMode.SUBQUERY是不适用的:

org.hibernate.AnnotationException:在ToOne关联中不允许使用FetchMode.SUBSELECT

我怎样才能解决这个问题?

EN

回答 3

Stack Overflow用户

发布于 2020-11-10 17:07:05

如果使用Spring Data JPA实现存储库,则可以在JPA实体中指定延迟抓取:

代码语言:javascript
运行
复制
@Entity
public class Project {
  @Id
  long id;

  @ManyToOne(fetch = FetchType.LAZY, optional = true)
  @JoinColumn(name = "customer", nullable = true, updatable = true)
  Customer customer;
}

@Entity
public class Customer {
  @Id
  long id;
...
}

并将@EntityGraph添加到Spring Data JPA-based存储库中:

代码语言:javascript
运行
复制
@Repository
public interface ProjectDao extends JpaRepository<Project, Long> {

    @EntityGraph(
            type = EntityGraphType.FETCH,
            attributePaths = { 
                    "customer" 
            }
    )
    Optional<Project> findById(Long id);
...
}

我在https://tech.asimio.net/2020/11/06/Preventing-N-plus-1-select-problem-using-Spring-Data-JPA-EntityGraph.html的博客文章帮助您使用Spring Data JPA@EntityGraph防止N+1选择问题。

票数 2
EN

Stack Overflow用户

发布于 2016-04-12 11:57:49

是的,这是n+1选择问题的一个活生生的例子.

在大多数情况下,我使用的方法是使关联变懒并定义一个batch size

或者,您可以使用JPQL查询和[left] join fetch直接从查询结果集初始化关联:

代码语言:javascript
运行
复制
select p from Project p left join fetch p.customer
票数 1
EN

Stack Overflow用户

发布于 2019-04-30 17:48:45

是的,正如@dragan-bozanovic所说,这是n+1选择问题的一个活生生的例子。

在Spring-Boot2.1.3中,可以使用@Fetch(FetchMode.JOIN)来解决这个问题:

代码语言:javascript
运行
复制
  @ManyToOne(optional = true)
  @Fetch(FetchMode.JOIN)
  @JoinColumn(name = "customer", nullable = true, updatable = true)
  Customer customer;

警告:如果关系可能无效,例如,当用@NotFound(action = NotFoundAction.IGNORE)标记时,每个无效关系都将触发另一个SELECT查询。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/36569318

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档