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

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

Jeśli chcesz być na bieżąco zapisz się na newsletter.

Zapisz się na newsletter

Mateusz Dąbrowski

Cześć jestem Mateusz, zajmuję się programowaniem już ponad 12 lat i zachęcam Cię do lektury mojego bloga

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *