我正在尝试这样做:
//...
class Person {
@ManyToMany(fetch = FetchType.EAGER)
@Fetch(FetchMode.JOIN)
private Set<Group> groups;
//...
}
当我通过Spring JPA存储库执行personRepository.findAll();
时,它会生成n + 1个查询,就好像我没有设置任何@Fetch
一样。 (一个查询首先获取所有人,然后每个人查询一次以获取组)。
尽管使用@Fetch(FetchMode.SUBSELECT)
正常工作!它只生成2个查询。 (一个适用于所有人,一个适用于团体)。因此,hibernate会对某些提取参数做出反应,而不是JOIN
。
我也试过删除EAGER
抓取而没有运气。
//...
class Person {
@ManyToMany()
@Fetch(FetchMode.JOIN)
private Set<Group> groups;
//...
}
我正在使用Spring JPA,这是我的存储库的代码:
public interface PersonRepository extends JpaRepository<Person, Long> {
}
JOIN是不是通过Spring JPA工作,还是我做错了什么?
答案 0 :(得分:21)
通过许多论坛和博客来阅读你的问题(我猜你可能在发布之前就已经这样做了)我也认为
如果使用Query接口,@Fetch(FetchMode.JOIN)将被忽略 (例如:session.createQuery())但如果你使用它将被正确使用 Criteria接口。
这实际上是Hibernate中的一个从未解决的错误。它是 很不幸,因为很多应用程序都使用Query接口 无法轻松迁移到Criteria界面。
如果使用Query接口,则必须始终添加JOIN FETCH 手动将语句转换为HQL。
答案 1 :(得分:9)
使用JPA 时我也无法使@Fetch(FetchMode.JOIN)
工作(虽然它在使用hibernate Criteria api时工作正常)但我也找不到任何解释原因的例子,但我可以想到一些解决方法。
急切加载群组的最直接方法是使用JPQL:
public interface PersonRepository extends JpaRepository<Person, String>{
@Query(value = "select distinct p from Person p left join fetch p.groups")
List<Person> getAllPersons();
}
当您使用spring-data-jpa时,您还可以使用Specification
热切地加载组。 (从1.4.x开始,您可以链接返回null的规范。)
final Specification<Person> fetchGroups = new Specification<Person>() {
@Override
public Predicate toPredicate(Root<Person> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
root.fetch("groups", JoinType.LEFT);
query.distinct(true);
return null;
}
};
如果这些都不是您的选择,那么您最好的选择可能就是使用@Fetch(FetchMode.SUBSELECT)
。
另一种选择是将@Fetch(FetchMode.SELECT)
与@BatchSize
结合使用。 @BatchSize
有助于解决n + 1个查询的问题。通过调整批量大小,您可以减少执行到CEIL(n / batch_size)+1的查询量。
@Entity
@Table(name = "persons")
public class Person {
@Id
String name;
@ManyToMany(fetch = FetchType.EAGER)
@BatchSize(size = 20)
Set<Group> groups = new HashSet<>();
}
@Entity
@Table(name = "groups")
public class Group {
@Id
String name;
@ManyToMany(mappedBy = "groups", fetch = FetchType.LAZY)
Set<Person> persons = new HashSet<>();
}
public interface PersonRepository extends JpaRepository<Person, String>{}
当您在包含10个人且personRepository.findAll();
设置为5的数据库上运行@BatchSize
时,此映射会生成以下sql。
Hibernate:
select
person0_.name as name1_
from
persons person0_
Hibernate:
select
groups0_.persons_name as persons1_1_1_,
groups0_.groups_name as groups2_1_,
group1_.name as name0_0_
from
persons_groups groups0_
inner join
groups group1_
on groups0_.groups_name=group1_.name
where
groups0_.persons_name in (
?, ?, ?, ?, ?
)
Hibernate:
select
groups0_.persons_name as persons1_1_1_,
groups0_.groups_name as groups2_1_,
group1_.name as name0_0_
from
persons_groups groups0_
inner join
groups group1_
on groups0_.groups_name=group1_.name
where
groups0_.persons_name in (
?, ?, ?, ?, ?
)
请注意,@BatchSize
也适用于使用FetchType.LAZY
映射的集合。