@Fetch(FetchMode.JOIN)
使用 JPA 时我也无法开始工作(尽管在使用 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>{}
personRepository.findAll();
当您在包含 10 个人并@BatchSize
设置为 5的数据库上运行时,此映射会产生以下 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
.