Nie bardzo rozumiem jak EAGER ma wpływać na szybkość wyjścia z transakcji i założenia projektowe?
Sorry, pomyliłem z FetchType.EAGER
Przecież chyba zawsze załadować lepiej to jako LAZY no nie? Nawet jeśli wiem, że będę operował na całej kolekcji bo np musze coś z niej zsumować to jak mam LAZY to mi po prostu pobierze kolekcje jak się do niej odwołam.
Nawet jeśli każda metody w encji potrzebuje kolekcji w środku to nadal nie widzę różnicy w praktyce między EAGER a LAZY. No chyba, że tu chodzi o jakieś optymalizację ?
Oczywiście, że chodzi o optymalizację. Np. taki kod:
Kopiuj
class MyService {
private final MySecondService service;
public Double compute() {
MyEntity entity = service.subEntity();
Predicate<MyJoinedEntity> myPredicate = createPredicate(entity.getName());
OptionalDouble sum = entity getJoinedEntities().stream()
.filter(myPredicate)
.mapToDouble(MyEntity::value)
.sum();
return sum.orElse(-1);
}
// Time consuming call
private Predicate<MyJoinedEntity> createPredicate(String name) {
// ...
}
}
class MySecondService {
@Transactional
public MyEntity subEntity() {
// ... fetch data
}
}
Oba są ogarniane przez Springa, więc jeśli jest @Transactional to transakcja jest zakładana na starcie i commitowana na końcu. Załóż że createPredicate(name) trwa bardzo długo, np. minutę. MyEntity ma pod spodem mapowanie do 1:N do JoinedEntity.
I teraz w linijce z entity.getJoinedEnitites() jeśli będzie FetchType.LAZY to w tej linijce poleci ci NPE - transakcja zakończy się przy wyjściu z MySecondService.subEntity(), a entity będzie w stanie detached.
Żeby to rozwiązać możesz:
- Dorzucić transakcję na
compute()
- Zamienić FetchType na EAGER.
- W subEntity() zrobić coś z kolekcją pod spodem, np. zawołać
entity.getJoinedEntities().size()
Opcja pierwsza blokuje transakcję - a więc jeśli nie zmieniłem ConnectionReleaseMode i połączenie w ConnectionPool dostępnym dla aplikacji - na czas wykonania createPredicate(name).
Opcja druga sprawę rozwiązuje.
Opcja trzecia też, tylko, że jest wolniejsza niż opcja druga bo zamiast od razu zaciągnąć wszystko konwersacja będzie miała dwa zapytania. Poza tym wygląda słabo - bo niby masz FetchType.LAZY, natomiast MySecondService będzie ładował to i tak od razu.