Cześć,
czytam sobie o problemie n+1 w Hibernate i po pierwsze nie jestem w stanie sprawić żeby u mnie w apce wystąpił ten problem. Przykładowo, stworzyłem sobie dwie klasy: User
i Feature
z relacją undirectional, czyli tylko w User entity mam:
@OneToMany
private List<Feature> features = new ArrayList<>();
Dodaję sobie jednego usera i kilka featerow przy odpalaniu apki w klasie Bootstrap. I mamy sobie klasę jakiśService
gdzie w pętli wypisuje właściwości featerow (nie zwracajcie uwagi na sposób pobierania optionala) czyli:
List<Feature> features = getUserById(id).get().getFeatures();
for (Feature feature : features) {
String description = feature.getDescription();
System.out.println( description);
}
Dodałem w application.properties
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
I odpalam requesta żeby się ta metoda serwisowa uruchomiła to w logach mam:
Hibernate:
select
user0_.id as id1_1_0_,
user0_.first_name as first_na2_1_0_,
user0_.last_name as last_nam3_1_0_
from
"user" user0_
where
user0_.id=?
Hibernate:
select
features0_."user_id" as user_id1_2_0_,
features0_.features_id as features2_2_0_,
feature1_.id as id1_0_1_,
feature1_.description as descript2_0_1_,
feature1_.enabled as enabled3_0_1_,
feature1_.feature_group as feature_4_0_1_,
feature1_.name as name5_0_1_
from
"user_features" features0_
inner join
feature feature1_
on features0_.features_id=feature1_.id
where
features0_."user_id"=?
To gdzie występuje problem w postaci osobnego zapytania do każdego z dzieci?
Powinno być coś takiego (przykład z jakiejś strony):
SELECT
pc.id AS id1_1_,
pc.post_id AS post_id3_1_,
pc.review AS review2_1_
FROM
post_comment pc
SELECT p.id AS id1_0_0_, p.title AS title2_0_0_ FROM post p WHERE p.id = 1
SELECT p.id AS id1_0_0_, p.title AS title2_0_0_ FROM post p WHERE p.id = 2
SELECT p.id AS id1_0_0_, p.title AS title2_0_0_ FROM post p WHERE p.id = 3
SELECT p.id AS id1_0_0_, p.title AS title2_0_0_ FROM post p WHERE p.id = 4
Druga sprawa to natrafiłem na rozwiązania typu:
-
@BatchSize
-
@Fetch(FetchMode.JOIN)
Pytanie co do drugiej opcji z fetchem. Z tego co zrozumiałem dodanie @Fetch(JOIN)
powoduje, że kolekcja jest pobierana od razu (eager, nie lazy). Skoro przy lazy loadingu mamy problem n+1 i załatwiamy go zmieniając pośrednio na EAGER (przez fetch) to czemu jest on polecany do dużych kolekcji ze względu na performance jak paradoksalnie tworzy to jeszcze większy problem wydajnościowy?