我有一个项目列表和一个客户列表。项目可以针对一个客户,每个客户都可以拥有多个项目。所以这是一个简单的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个客户查询。
这很快就会增加,对于大型项目/客户列表,我们的应用程序变得相当慢。这只是一个例子。我们所有具有关系的实体都会受到此行为的影响。
我已经在@Fetch(FetchMode.JOIN)
字段上尝试了customers
,如here所建议的那样,但它没有做任何事情,根据Hibernate,FetchMode.SUBQUERY
不适用:
org.hibernate.AnnotationException:在ToOne关联上不允许使用FetchMode.SUBSELECT
我该如何解决这个问题?
是的,这是n + 1选择问题的一本书的例子。
我在大多数情况下使用的方法是使关联变得懒惰并定义batch size。
或者,您可以使用带有[left] join fetch
的JPQL查询来直接从查询结果集初始化关联:
select p from Project p left join fetch p.customer
是的,正如@ 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
查询。