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:
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.