Biorąc pod uwagę następujący model domeny, chcę załadować wszystkie Answer
s, w tym ich Value
s i ich podrzędne, i umieścić go w, AnswerDTO
aby następnie przekonwertować na JSON. Mam działające rozwiązanie, ale cierpi na problem N + 1, którego chcę się pozbyć za pomocą ad-hoc @EntityGraph
. Wszystkie powiązania są skonfigurowane LAZY
.
@Query("SELECT a FROM Answer a")
@EntityGraph(attributePaths = {"value"})
public List<Answer> findAll();
Używając metody ad-hoc @EntityGraph
w Repository
metodzie, mogę upewnić się, że wartości są wstępnie pobierane, aby zapobiec N + 1 w Answer->Value
powiązaniu. Chociaż mój wynik jest w porządku, jest jeszcze jeden problem N + 1, z powodu leniwego ładowania selected
skojarzenia MCValue
s.
Za pomocą tego
@EntityGraph(attributePaths = {"value.selected"})
kończy się niepowodzeniem, ponieważ selected
pole jest oczywiście tylko częścią niektórych Value
podmiotów:
Unable to locate Attribute with the the given name [selected] on this ManagedType [x.model.Value];
Jak mogę powiedzieć WZP, że próbuje pobrać selected
powiązanie tylko w przypadku, gdy jest to wartość MCValue
? Potrzebuję czegoś takiego optionalAttributePaths
.
źródło
selected
dla odpowiedzi, które mająMCValue
. Nie podobało mi się, że wymagałoby to dodatkowej pętli i musiałem zarządzać mapowaniem między zestawami danych. Podoba mi się twój pomysł wykorzystania w tym celu pamięci podręcznej Hibernacji. Czy potrafisz wyjaśnić, na ile bezpieczne (pod względem spójności) jest poleganie na pamięci podręcznej w celu przechowywania wyników? Czy to działa, gdy zapytania są dokonywane w transakcji? Boję się trudnych do wykrycia i sporadycznych, leniwych błędów inicjalizacji.MCValue
encje. I nie potrzebujesz dodatkowej pętli. Powinieneś pobrać wszystkieMCValue
encje za pomocą 1 zapytania, które łączy się zAnswer
tą samą klauzulą WHERE, co bieżące zapytanie. Mówiłem o tym również w dzisiejszym streamie na żywo: youtu.be/70B9znTmi00?t=238 Zaczęło się o 3:58, ale zadałem kilka innych pytań pomiędzy ...SINGLE_TABLE_INHERITANCE
.Nie wiem, co tam robi Spring-Data, ale aby to zrobić, zwykle musisz użyć
TREAT
operatora, aby uzyskać dostęp do sub-asocjacji, ale implementacja dla tego operatora jest dość błędna. Hibernacja obsługuje niejawny dostęp do właściwości podtypu, który byłby tutaj potrzebny, ale najwyraźniej Spring-Data nie obsługuje tego poprawnie. Polecam rzucić okiem na Blaze-Persistence Entity-Views , bibliotekę, która działa na bazie JPA, która pozwala mapować dowolne struktury na podstawie modelu encji. Możesz zmapować swój model DTO w sposób bezpieczny dla typu, również w strukturze dziedziczenia. Widoki encji dla Twojego przypadku użycia mogą wyglądać następującoDzięki wiosennej integracji danych zapewnianej przez Blaze-Persistence możesz zdefiniować takie repozytorium i bezpośrednio wykorzystać wynik
Wygeneruje zapytanie HQL, które wybiera tylko to, co zostało zamapowane,
AnswerDTO
co jest podobne do następującego.źródło
interface MCValueDTO extends ValueDTO { @Mapping("selected.id") Set<Long> getOption(); }
Mój najnowszy projekt wykorzystywał GraphQL (pierwszy dla mnie) i mieliśmy duży problem z zapytaniami N + 1 i próbowałem zoptymalizować zapytania, aby łączyć się tylko dla tabel, gdy są one wymagane. Uważam, że Cosium / spring-data-jpa-entity-graph jest niezastąpiony. Rozszerza
JpaRepository
i dodaje do zapytania metody przekazywania wykresu encji. Następnie można budować dynamiczne wykresy encji w czasie wykonywania, aby dodać lewe sprzężenia tylko dla potrzebnych danych.Nasz przepływ danych wygląda mniej więcej tak:
Aby rozwiązać problem
__typename
niewłączania niepoprawnych węzłów do wykresu encji (na przykład z graphql), stworzyłem klasę narzędziową, która obsługuje generowanie wykresu encji. Klasa wywołująca przekazuje nazwę klasy, dla której generuje wykres, który następnie sprawdza poprawność każdego węzła na wykresie względem metamodelu obsługiwanego przez ORM. Jeśli węzeł nie znajduje się w modelu, usuwa go z listy węzłów wykresu. (Ta kontrola musi być rekurencyjna i sprawdzać także każde dziecko)Zanim to znalazłem, wypróbowałem projekcje i każdą inną alternatywę zalecaną w dokumentach Spring JPA / Hibernate, ale nic nie wydawało się rozwiązać problemu elegancko lub przynajmniej z dużą ilością dodatkowego kodu
źródło
selected
powiązanie nie jest dostępne dla wszystkich podtypówvalue
.Edytowane po twoim komentarzu:
Przepraszam, nie zastanawiałem się nad problemem w pierwszej rundzie, problem pojawia się przy uruchomieniu danych wiosny, nie tylko podczas próby wywołania findAll ().
Możesz teraz nawigować po pełnym przykładzie, który możesz pobrać z mojego github: https://github.com/bdzzaid/stackoverflow-java/blob/master/jpa-hibernate/
Możesz łatwo odtworzyć i naprawić swój problem w tym projekcie.
W rzeczywistości dane Spring i hibernacja nie są w stanie domyślnie określić „wybranego” wykresu i należy określić sposób zbierania wybranej opcji.
Najpierw musisz zadeklarować NamedEntityGraphs klasy Answer
Jak widać, istnieją dwa NamedEntityGraph dla wartości atrybutu klasy Answer
Pierwszy dla wszystkich Wartość bez określonego związku do załadowania
Drugi dla konkretnej wartości Multichoice . Jeśli go usuniesz, odtworzysz wyjątek.
Po drugie, musisz być w kontekście transakcyjnym answerRepository.findAll (), jeśli chcesz pobierać dane typu LAZY
źródło
value
-association sięAnswer
jednak corazselected
stowarzyszenie w przypadku, gdyvalue
jestMCValue
. Twoja odpowiedź nie zawiera żadnych informacji na ten temat.OneToMany
jako,FetchType.EAGER
ale jak stwierdzono w pytaniu: wszystkie skojarzenia sąLAZY
.selected
dla każdej odpowiedzi zamiast ładować je z góry.