这是一个N + 1问题,我该如何解决?

时间:2016-04-12 09:36:40

标签: java hibernate jpa query-performance

我有一个项目列表和一个客户列表。项目可以针对一个客户,每个客户都可以拥有多个项目。因此,这是一个简单的1:n关系,项目是拥有者。

简化为必要的

@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个客户查询。

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

我已根据建议here@Fetch(FetchMode.JOIN)字段上尝试了customers,但它没有做任何事情,FetchMode.SUBQUERY根据Hibernate不适用:

  

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

如何解决此问题?

3 个答案:

答案 0 :(得分:1)

是的,这是n + 1选择问题的一本书的例子。

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

或者,您可以使用带有ggplot(data=trees, aes(Volume)) + stat_ecdf() 的JPQL查询来直接从查询结果集初始化关联:

[left] join fetch

答案 1 :(得分:0)

是的,这是@ dragan-bozanovic所说的n + 1选择问题的书本示例。

在Spring-Boot 2.1.3中,@Fetch(FetchMode.JOIN)可用于解决该问题:

  @ManyToOne(optional = true)
  @Fetch(FetchMode.JOIN)
  @JoinColumn(name = "customer", nullable = true, updatable = true)
  Customer customer;

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

答案 2 :(得分:0)

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

@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的存储库中:

@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选择问题。