Cześć,
Mam problem, z którym zmagam się od jakiegoś czasu i liczę na Waszą pomoc.
Posiadam abstrakcyjną klasę Person, która jest encją, po której dziedziczą trzy inne klasy: Teacher, Principal i StaffMember – wszystkie również są encjami. Wszystkie te trzy typy osób są przechowywane w jednej tabeli Person, a zarządzam nimi za pomocą wspólnego repozytorium PersonRepository.
Klasa Person zawiera pola wspólne dla wszystkich typów osób, takie jak imię, nazwisko, PESEL, email, itd. Natomiast każda z dziedziczących klas ma swoje specyficzne pola – np. yearsAsPrincipal dla dyrektora, czy assignedClasses (lista klas) dla nauczyciela. Dodatkowo, jedna z tych klas (Teacher) posiada relację OneToMany z inną encją.
Potrzebowałem stworzyć metodę, która pozwoli mi na wyszukiwanie osób w repozytorium Person po różnych kryteriach – zarówno tych wspólnych, jak i specyficznych dla danej klasy. Wykorzystałem w tym celu Specification z JPA. Wstępnie wydawało się, że wszystko działa poprawnie – testy przechodziły, zapytania w Postmanie zwracały oczekiwane wyniki.
Problem pojawia się przy wyszukiwaniu osób w klasie, która posiada relację OneToMany, występuje wówczas n+1 zapytań. Zamiast jednego SELECT-a, który zwróciłby wszystkie potrzebne dane, dla każdej osoby z relacją generowane jest osobne zapytanie do bazy. Przykładowo, dla 1000 osób, z których 300 to nauczyciele posiadający przypisane klasy, zamiast jednego zapytania SQL, wykonuje się 1 + 300 zapytań.
Dla klas bez relacji wszystko działa prawidłowo – jedno zapytanie wystarcza, aby pobrać wszystkie dane. Natomiast dla klasy z relacją występuje problem n+1.
Gdyby klasa Person nie była abstrakcyjna, mógłbym łatwo rozwiązać ten problem, dodając do repozytorium odpowiednie metody findAll z LEFT JOIN FETCH oraz projekcję, aby od razu zwracać PersonDTO. Niestety, w przypadku klasy abstrakcyjnej, która łączy różne typy encji, sprawa się komplikuje.
Zastanawiam się nad zastosowaniem trzech oddzielnych metod findAll dla każdej z dziedziczących klas, a następnie scalaniem wyników w całość, ale obawiam się, że może to nie być najbardziej wydajne rozwiązanie. Czy ktoś spotkał się z podobnym problemem i ma jakieś sugestie, jak to efektywnie rozwiązać?
Będę wdzięczny za każdą wskazówkę.
Pozdrawiam!