我有一个项目列表和一个客户列表。项目可以针对一个客户,每个客户都可以拥有多个项目。因此,这是一个简单的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
如何解决此问题?
答案 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选择问题。