trzy rzeczy hibernate

Trzy rzeczy, które powinieneś wiedzieć o Hibernate

W tym artykule postaram się przybliżyć wam trzy zagadnienia związane z Hibernate’em. Moim zdaniem są to najważniejsze aspekty, które każdy programista używający tego narzędzia powinien znać.

1. Problem N+1 zapytań

Często programiści zaczynający swoją przygodę z Hibernate’em są nieświadomi pewnych mechanizmów, których na pierwszy rzut oka nie widać. Niejako przekonani o pewnego rodzaju auto-magiczności Hibernate’a wpadają w pułapkę N+1 zapytań. Problem ten jest dosyć powszechny i nawet bardziej zaawansowani programiści często się z nim borykają.

Dotyczy on pobierania kolekcji powiązanych z daną encją. Zwykle, w relacjach jeden-do-wielu (one-to-many). Jego efektem jest drastyczny spadek wydajności, wynikający z generowania bardzo dużej ilości zapytań sql.

Przykład:

Kiedy pobieramy listę użytkowników i chcemy także pobrać powiązane z nimi adresy (np. korespondencyjny, zamieszkania), w domyślnej konfiguracji Hibernate dla każdej kolekcji adresów wykona dodatkowe zapytanie (per użytkownik). Jeśli chcemy pobrać listę 100 użytkowników to Hibernate wykona jedno zapytanie, żeby pobrać użytkowników (1 – zapytanie) i jeśli odczytamy dodatkowo dla każdego użytkownika listę adresów, to Hibernate wykona dodatkowo 100 zapytań po jednym dla każdego użytkownika (n – zapytań, gdzie n oznacza ilość użytkowników w tym przypadku 100).

Warto tutaj zwrócić uwagę, że dodatkowe zapytania zostaną wykonane tylko wtedy gdy odczytamy listę adresów dla danego użytkownika. Dzieję się tak dlatego, że domyślnie Hibernate „doładowuje” dane „leniwie” (lazy). Jest to optymalizacja, która sprawia, że dane są dociągane tylko wtedy, gdy są potrzebne. Generalnie jest to dobre rozwiązanie, kiedy potrzebujemy powiązanych danych sporadycznie. W innym przypadku wpadamy w pułapkę n + 1 zapytań.

2. FetchType LAZY i EAGER

Kolejną rzeczą, którą warto wiedzieć o Hibernate to FetchType, czyli sposób pobierania encji w relacjach. Jeśli weźmiemy przykładową relację: jeden-do-wielu(one-to-many), to Hibernate może pobrać obiekty po stronie many na dwa sposoby:

FetchType.LAZY  – pobieramy dane dopiero wtedy, gdy ich potrzebujemy. W praktyce wtedy, gdy użyjemy gettera na powiązanej kolekcji, Hibernate wykonuje zapytanie do bazy danych.

FetchType.EAGER – pobieramy dane, gdy zostaje wykonane zapytanie pobierające nadrzędną część relacji.

FetchType możesz ustawić poprzez adnotacje; różne relacje mają różnie ustawiony domyślny FetchType:

  • @OneToOneFetchType.EAGER
  • @OneToManyFetchType.LAZY
  • @ManyToOneFetchType.EAGER
  • @ManyToManyFetchType.LAZY

Można go oczywiście też zmieniać poprzez parametr adnotacji np. @OneToMany(fetch = FetchType.EAGER). To jak go należy ustawiać, zależy zawsze od sytuacji.

3. Fetch Mode

FetchMode ustawiamy za pomocą adnotacji np. @Fetch(FetchMode.JOIN)

Domyślnie encje powiązane pobierane są poprzez FetchMode.SELECT (tak, jak opisałem to w punkcie 1). Tryb ten można dodatkowo sparametryzować używając adnotacji @BatchSize(size = 25). Sprawia to, że powiązane encje będą pobierane dodatkowymi zapytaniami, ale każde zapytanie będzie pobierało określoną ilość powiązanych encji (np. 25, w sqlu, który generuje Hibernate jest to odwzorowane za pomocą klauzuli in(...))

Poza domyślnym sposobem pobierania relacji są jeszcze dwa: FetchMode.JOIN i FetchMode.SUBSELECT

FetchMode.JOIN sprawia, że powiązane kolekcje są pobierane wraz z główną encją i dzieje się to poprzez użycie klauzuli JOIN w sqlu. Dodatkowo musimy zapewnić unikalność zwróconych rezultatów stosując np. Set.

FetchMode.SUBSELECT jest ustawieniem, dzięki któremu Hibernate jednym zapytaniem pobiera listę encji np. użytkowników, a kolejne zapytanie pobiera listę wszystkich powiązanych encji np. adresów. Przy czym są pobierane wszystkie adresy, bez względu na to, czy został pobrany odpowiedni użytkownik dla danego adresu.

Główna różnica, która odróżnia FetchType od FetchMode jest to, że FetchType dotyczy tego czy pobrać daną encję, a FetchMode tego jak pobrać daną encję w relacji.

Podsumowanie

Opisałem tu trzy najważniejsze zagadnienia związane z Hibernate. W następnych częściach postaram się przybliżyć każdy z tematów z osobna w bardziej szczegółowej formie opisuje:

Hibernate i problem N + 1 zapytań

FetchMode w Hibernate, czyli jak pobierać dane

 

 

Mateusz Dąbrowski

Cześć jestem Mateusz, zajmuję się programowaniem już ponad 12 lat z czego ponad 8 programuję w Javie. Zapraszam Cię do lektury mojego bloga. Możesz przeczytać więcej o mnie >>TUTAJ<<

3 thoughts to “Trzy rzeczy, które powinieneś wiedzieć o Hibernate”

  1. Mateusz mam pytanie odnośnie FetchType.LAZY.

    Mam pole
    @NotNull
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumns({
    @JoinColumn(name = „aId”),
    @JoinColumn(name = „bid”)
    })
    private A a ;

    Wywołanie gettera na tym polu nie powoduje, że obiekt A zostanie „dociągniety” dopiero zmiana FetchType.EAGER dowiązuje obiekt. Używam Lomboka mam Gettera na tym polu.

    1. Nie wiem jak to pobierasz. Nie wiem też, czy Lombok coś tutaj psuje, chociaż wydaje mi się, że nie powinien. Trudno coś powiedzieć bez kod.

      1. samo wywołanie gettera nie pobiera jeszcze encji. dopiero jak wykonasz jakaś operację na pobranym obiekcie np wywołasz:
        System.out.prinln(pobbranyObiekt); .

Komentarze są zamknięte.