Mam klasę Person:
@Entity
public class Person {
@Id
@GeneratedValue
private Long id;
@ManyToMany(fetch = FetchType.LAZY)
private List<Role> roles;
// etc
}
Z leniwą relacją wiele do wielu.
W moim kontrolerze mam
@Controller
@RequestMapping("/person")
public class PersonController {
@Autowired
PersonRepository personRepository;
@RequestMapping("/get")
public @ResponseBody Person getPerson() {
Person person = personRepository.findOne(1L);
return person;
}
}
A PersonRepository to tylko ten kod, napisany zgodnie z tym przewodnikiem
public interface PersonRepository extends JpaRepository<Person, Long> {
}
Jednak w tym kontrolerze faktycznie potrzebuję leniwych danych. Jak mogę uruchomić jego ładowanie?
Próba uzyskania do niego dostępu zakończy się niepowodzeniem
nie udało się leniwie zainicjować kolekcji ról: no.dusken.momus.model.Person.roles, nie można zainicjować serwera proxy - brak sesji
lub inne wyjątki w zależności od tego, co próbuję.
Mój opis XML , w razie potrzeby.
Dzięki.
Person
obiekt z jakimś parametrem? W tymQuery
uwzględnijfetch
klauzulę i załadujRoles
też dla osoby.Odpowiedzi:
Będziesz musiał wykonać jawne wywołanie leniwej kolekcji, aby ją zainicjować (powszechną praktyką jest wywoływanie
.size()
w tym celu). W Hibernate istnieje metoda this (Hibernate.initialize()
), ale JPA nie ma jej odpowiednika. Oczywiście będziesz musiał upewnić się, że wywołanie zostało wykonane, gdy sesja jest nadal dostępna, więc dodaj adnotację do metody kontrolera z@Transactional
. Alternatywą jest utworzenie pośredniej warstwy usług między kontrolerem a repozytorium, która mogłaby ujawnić metody inicjujące leniwe kolekcje.Aktualizacja:
Należy pamiętać, że powyższe rozwiązanie jest łatwe, ale skutkuje dwoma różnymi zapytaniami do bazy danych (jedno dla użytkownika, drugie dla jego ról). Jeśli chcesz osiągnąć lepszą wydajność, dodaj następującą metodę do interfejsu repozytorium Spring Data JPA:
Ta metoda wykorzysta klauzulę fetch join w JPQL, aby z niecierpliwością załadować skojarzenie ról w jednej rundzie do bazy danych, a zatem zmniejszy spadek wydajności poniesiony przez dwa różne zapytania w powyższym rozwiązaniu.
źródło
join
bezfetch
, zestaw zostanie zwrócony zinitialized = false
; dlatego nadal wysyła drugie zapytanie po uzyskaniu dostępu do zestawu.fetch
jest kluczem do upewnienia się, że relacja jest całkowicie załadowana i uniknięcia drugiego zapytania.Chociaż jest to stary post, rozważ użycie @NamedEntityGraph (Persistence Javax) i @EntityGraph (Spring Data JPA). Połączenie działa.
Przykład
a następnie repozytorium wiosenne, jak poniżej
źródło
@NamedEntityGraph
jest to część API JPA 2.1, która nie jest zaimplementowana w Hibernate przed wersją 4.3.0.@EntityGraph(attributePaths = "employeeGroups")
może być używany bezpośrednio w repozytorium danych Spring do dodawania adnotacji do metody bez potrzeby umieszczania@NamedEntityGraph
kodu na @Entity - mniej kodu, łatwy do zrozumienia po otwarciu repozytorium.Masz kilka opcji
Więcej pracy, najlepsza wydajność.
Mniej pracy, zwykle akceptowalne w środowiskach internetowych.
Mniej pracy, przydatne, gdy OEMIV nie jest dostępny, na przykład w aplikacji Swing, ale może być również przydatny w implementacjach repozytorium, aby zainicjować dowolną jednostkę za jednym razem.
W przypadku ostatniej opcji napisałem klasę narzędziową JpaUtils do inicjowania jednostek w pewnym deph.
Na przykład:
źródło
może być ładowany leniwie tylko podczas transakcji. Możesz więc uzyskać dostęp do kolekcji w swoim repozytorium, w którym znajduje się transakcja - lub to , co zwykle robię, to
get with association
lub ustawiam fetchmode na Eager.źródło
Myślę, że potrzebujesz OpenSessionInViewFilter, aby sesja była otwarta podczas renderowania widoku (ale nie jest to zbyt dobra praktyka).
źródło
Dane wiosenne
JpaRepository
Spring Data
JpaRepository
definiuje następujące dwie metody:getOne
, która zwraca serwer proxy jednostki, który jest odpowiedni do ustawienia powiązania@ManyToOne
lub@OneToOne
nadrzędnego podczas utrwalania jednostki podrzędnej .findById
, która zwraca jednostkę POJO po uruchomieniu instrukcji SELECT, która ładuje jednostkę z powiązanej tabeliJednak w Twoim przypadku nie zadzwoniłeś ani
getOne
teżfindById
:Więc zakładam, że
findOne
metoda jest metodą zdefiniowaną wPersonRepository
. JednakfindOne
metoda nie jest zbyt przydatna w twoim przypadku. Ponieważ musisz pobrać kolekcjęPerson
wraz z isroles
, lepiejfindOneWithRoles
zamiast tego użyć metody.Niestandardowe metody Spring Data
Możesz zdefiniować
PersonRepositoryCustom
interfejs w następujący sposób:I zdefiniuj jego implementację w ten sposób:
Otóż to!
źródło
Możesz zrobić to samo w ten sposób:
Po prostu użyj faqQuestions.getFaqAnswers (). Size () nin swojego kontrolera, a otrzymasz rozmiar, jeśli leniwie zainicjujesz listę, bez pobierania samej listy.
źródło