Java FetchMode join 对 spring JPA 存储库中的多对多关系没有影响
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18891789/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
FetchMode join makes no difference for ManyToMany relations in spring JPA repositories
提问by Matsemann
I'm trying to do this:
我正在尝试这样做:
//...
class Person {
@ManyToMany(fetch = FetchType.EAGER)
@Fetch(FetchMode.JOIN)
private Set<Group> groups;
//...
}
it generates n+1 queries when I do personRepository.findAll();
through a Spring JPA repository, just as if I didn't have any @Fetch
set. (One query first to get all the persons, and then one query per person to fetch the groups).
当我personRepository.findAll();
通过 Spring JPA 存储库执行时,它会生成 n+1 个查询,就像我没有任何@Fetch
设置一样。(首先查询获取所有人员,然后每个人查询获取组)。
Using @Fetch(FetchMode.SUBSELECT)
works, though! It only generates 2 queries. (One for all persons, and then one for the groups). So hibernate reacts to somefetch parameters, just not the JOIN
.
但是,使用@Fetch(FetchMode.SUBSELECT)
作品!它只生成 2 个查询。(一个用于所有人,然后一个用于团体)。所以 hibernate 对一些fetch 参数做出反应,而不是JOIN
.
I have also tried removing the EAGER
fetching with no luck.
我也试过在EAGER
没有运气的情况下删除提取。
//...
class Person {
@ManyToMany()
@Fetch(FetchMode.JOIN)
private Set<Group> groups;
//...
}
I am using Spring JPA, and this is the code for my repository:
我正在使用 Spring JPA,这是我的存储库的代码:
public interface PersonRepository extends JpaRepository<Person, Long> {
}
Does JOIN just not work through Spring JPA, or am I doing something wrong?
JOIN 只是不能通过 Spring JPA 工作,还是我做错了什么?
回答by DarkHorse
Going through many forums and blogs to read for your problem (I guess you might have done that before posting it here) I too think that
通过许多论坛和博客阅读您的问题(我想您可能在将其发布到此处之前已经这样做了)我也认为
@Fetch(FetchMode.JOIN) will be ignored if you use the Query interface (e.g.: session.createQuery()) but it will be properly used if you use the Criteria interface.
This is practically a bug in Hibernate which was never resolved. It is unfortunate because a lot of applications use the Query interface and cannot be migrated easily to the Criteria interface.
If you use the Query interface you always have to add JOIN FETCH statements into the HQL manually.
如果您使用 Query 接口(例如:session.createQuery()),@Fetch(FetchMode.JOIN) 将被忽略,但如果您使用 Criteria 接口,它将被正确使用。
这实际上是 Hibernate 中从未解决的错误。很不幸,因为很多应用程序使用 Query 接口,无法轻松迁移到 Criteria 接口。
如果您使用 Query 接口,您总是必须手动将 JOIN FETCH 语句添加到 HQL 中。
回答by Pieter
I also couldn't get @Fetch(FetchMode.JOIN)
to work when using JPA (although it works fine when using the hibernate Criteria api)and I also couldn't find any examples explaining why, but I can think of a few workarounds.
@Fetch(FetchMode.JOIN)
使用 JPA 时我也无法开始工作(尽管在使用 hibernate Criteria api 时它工作正常)并且我也找不到任何解释原因的示例,但我可以想到一些解决方法。
The most straightforward way to load the Groups eagerly, is to use JPQL:
急切加载组的最直接方法是使用 JPQL:
public interface PersonRepository extends JpaRepository<Person, String>{
@Query(value = "select distinct p from Person p left join fetch p.groups")
List<Person> getAllPersons();
}
As you are using spring-data-jpa, you could also load the Groups eagerly by using a Specification
. (As of 1.4.x you can chain specs that return null).
当您使用 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;
}
};
If neither of these is an option for you, your best bet is probably to use @Fetch(FetchMode.SUBSELECT)
.
如果这些都不适合您,那么最好的选择可能是使用@Fetch(FetchMode.SUBSELECT)
.
Another option is to use @Fetch(FetchMode.SELECT)
in combination with @BatchSize
. @BatchSize
helps to solve the problem of the n+1 queries. By tweaking the batch size you can reduce the amount of queries executed to CEIL(n/batch_size)+1.
另一种选择是@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>{}
This mapping results in the following sql when you run personRepository.findAll();
on a database containing 10 persons and @BatchSize
set to 5.
当您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 (
?, ?, ?, ?, ?
)
Note that @BatchSize
also works for collections mapped with FetchType.LAZY
.
请注意,这@BatchSize
也适用于使用FetchType.LAZY
.