Java Developer? Przejd藕 na wy偶szy poziom wiedzy 馃敟馃挭 聽Sprawd藕

Team Leader? Podnie艣 efektywno艣膰 swojego zespo艂u 馃憣聽Sprawd藕

Sposoby na popraw臋 wydajno艣ci Hibernate 馃殌

utworzone przez Hibernate, Java, Tip and Tricks

Hibernate jest rozbudowanym narz臋dziem u艂atwiaj膮cym prac臋 z bazami danych w Java. Wiele operacji wykonywanych jest „auto magicznie”. Mo偶esz mie膰 wra偶enie, 偶e „To” dzia艂a samoistnie oraz szybko i wydajnie.

Nieznajomo艣膰 specyfiki narz臋dzia mo偶e doprowadzi膰 do tego, 偶e wydajno艣膰 Twojej aplikacji spadnie dramatycznie.

鉃★笍 Je艣li poznasz i zrozumiesz zasady dzia艂ania, b臋dziesz w stanie poprawi膰 wydajno艣膰 operacji zwi膮zanych z komunikacj膮 z baz膮 danych.

鉃★笍 Wiedz膮c jak w艂膮czy膰 monitorowanie, zweryfikujesz czy zapytania wysy艂ane do bazy danych s膮 tymi, kt贸rych si臋 spodziewa艂e艣.

Z tego artyku艂u dowiesz si臋:

  • Jak dzia艂a Lazy-Loading 鈴憋笍 i czym r贸偶ni si臋 pobieranie LAZY od EAGER
  • Poznasz jak to dzia艂a w r贸偶nych typach relacji 馃
  • Dowiesz si臋, jak poprawi膰 wydajno艣膰 aplikacji 馃挭:
    • Eliminuj膮c N+1 problem
    • Przy operacjach masowych
    • W艂膮czaj膮c diagnostyk臋 i 艣ledzenie zapyta艅 SQL
    • Pobieraj膮c tylko to, czego potrzebujesz

Wydajno艣膰 Hibernate

Tw贸rz szybko dzia艂aj膮ce aplikacje z wydajn膮 i zoptymalizowan膮 obs艂ug膮 bazy danych.

Jak dzia艂a lazy loading?

Op贸藕nione 艂adowanie (lazy loading) w Hibernate dzia艂a w ten spos贸b, 偶e odpowiednio oznaczone pola klasy s膮 doczytywane z bazy danych dopiero przy pierwszej pr贸bie odczytu danych innych ni偶 klucz g艂贸wny. Hibernate wykorzystuje tzw. proxy, kt贸re „przykrywa” klas臋 encji wywo艂uj膮c dodatkow膮 logik臋. Przyk艂ad encje Card i User, powi膮zane ze sob膮 relacj膮 dwukierunkow膮 typu lazy.

艁adowanie op贸偶nione w Hibernate
艁adowanie op贸藕nione w Hibernate

Op贸藕nione 艂adowanie dotyczy膰 mo偶e:

  • Pola reprezentuj膮cego warto艣膰 kolumny tabeli.
  • Kolekcji reprezentuj膮cej relacj臋 lub powi膮zane dane z innej tabeli.

Kod Java dla przyk艂adu przytoczonego wcze艣niej

Lazy loading - kod Java
Lazy loading – kod Java

Dla por贸wnania tre艣膰 zapytania wygenerowanego przez Hibernate w przypadku, gdy:

  • Wywo艂amy wyszukiwanie encji Cardsession.get(Card.class, cardId)
  • Zamiast FetchType.LAZY zastosujemy FetchType.EAGER
select
        card0_.CARD_ID as card_id1_0_0_,
        card0_.COUNTRY as country2_0_0_,
        card0_.CARD_OWNER_ID as card_own5_0_0_,
        card0_.CARD_UUID as card_uui3_0_0_,
        card0_.ENABLED as enabled4_0_0_,
        user1_.USER_ID as user_id1_1_1_,
        user1_.LAST_NAME as last_nam2_1_1_,
        user1_.FIRST_NAME as first_na3_1_1_ 
    from
        CARDS card0_ 
    left outer join
        USERS user1_ 
            on card0_.CARD_OWNER_ID=user1_.USER_ID 
    where
        card0_.CARD_ID=?

Dla przypadku z u偶yciem FetchType.EAGER (po obydwu stronach relacji) nie b臋dzie osobnego doczytania danych u偶ytkownika dla pobrania imienia:

card.getOwner().getFirstName().

Stanie si臋 to w jednym przebiegu – co wida膰 w tre艣ci zapytania.

Lazy loading mo偶e poprawi膰 lub pogorszy膰 wydajno艣膰 aplikacji, zale偶y od tego, co w danym momencie chcesz zrobi膰.

S艂ynny problem N+1

Co to jest „problem N+1”?

Problem N+1 to sytuacja, w kt贸rej dla ka偶dego elementu z listy encji nast臋puje wys艂anie dodatkowego pojedynczego zapytania do bazy danych. Wyobra藕 sobie nasz przyk艂ad powy偶ej z kartami i u偶ytkownikami.

  • Wyszukujesz karty – wynik to 10 kart (jedno zapytanie SQL)
  • Dla ka偶dej z kart zostaje wys艂ane osobne zapytanie o w艂a艣ciciela (10 zapyta艅 SQL).

W sumie wykona艂o si臋 11 zapyta艅 – i to jest w艂a艣nie problem N+1 – dla ka偶dego z rekord贸w wyniku (jest ich N) zostaje wywo艂ane jedno dodatkowe zapytanie (+1).

Pojedyncze wykonywanie zapyta艅 powoduje du偶y narzut czasu zwi膮zany z komunikacj膮 (du偶o ma艂ych zapyta艅), a je艣li N jest du偶e, to aplikacja zaczyna spowalnia膰.

Kierunek relacji

Pierwsz膮 rzecz膮, kt贸r膮 nale偶y uwzgl臋dni膰, jest to czy relacja jest dwukierunkowa, czy jednokierunkowa.

Powy偶ej widzia艂e艣 przyk艂ady dla relacji dwukierunkowej, dla kt贸rej Hibernate w zale偶no艣ci od typu doci膮gania wygenerowa艂o jedno lub dwa zapytania, dla pobrania danych o dw贸ch typach – encje Card i User.

Ustawienie doci膮gania danych dla relacji

Kolejn膮 rzecz膮 jest zachowanie Hibernate dla doci膮gania relacji, a w szczeg贸lno艣ci domy艣lne warto艣ci dla poszczeg贸lnych anotacji – w艂a艣ciwo艣膰 fetch() o typie FetchType:

  • @OneToOne - default EAGER.
  • @ManyToOne - default EAGER.
  • @OneToMany - default LAZY.
  • @ManyToMany - default LAZY.

Jak wida膰 dwie pierwsze reprezentuj膮ce relacje jeden do jednego i wiele do jednego maj膮 warto艣膰 EAGER, co spowoduje, i偶 dane zostan膮 doci膮gni臋te od razu. Z kolei dwie pozosta艂e maj膮 warto艣膰 domy艣ln膮 LAZY. Musisz zachowa膰 czujno艣膰 i upewni膰 si臋, 偶e zachowanie doci膮gania relacji b臋dzie w艂a艣ciwe (u偶ywaj膮c domy艣lnych warto艣ci lub wymuszaj膮c odpowiednie).

Problem N+1 dla relacji typu EAGER

Dla doczytywania typu EAGER problem N+1 pojawia si臋 dla relacji jednokierunkowej typu @OneToOne oraz @ManyToOne.

Podczas mapowania danych przez Hibernate dla ka偶dej z encji Hibernate automatycznie wykona doczytanie danych – pojedynczo dla ka偶dego z rekord贸w zostanie doczytana encja z relacji o ile nie istnieje w kontek艣cie persystencji.

Mamy logik臋 wyszukiwania wszystkich kart dla kraju EA. W bazie danych jest 5 rekord贸w, ka偶da z kart ma innego w艂a艣ciciela.

Relacja ManyToOne EAGER
Relacja ManyToOne EAGER

Dla wyszukiwania

public List<CardEagerUnidirectional> findAll() {
        try {
            return session.createQuery("FROM CardEagerUnidirectional where cardCountry='EA'",
                                       CardEagerUnidirectional.class).getResultList();
        } catch (Exception e) {
            throw new DatabaseOperationException(e);
        }
    }

W konsoli zostan膮 wy艣wietlone nast臋puj膮ce zapytania

[Hibernate] select cardeageru0_.CARD_ID as card_id1_0_, ..... from CARDS cardeageru0_ where cardeageru0_.COUNTRY='EA'
[Hibernate] select userunidir0_.USER_ID as user_id1_1_0_, ..... from USERS userunidir0_ where userunidir0_.USER_ID=?
[Hibernate] select userunidir0_.USER_ID as user_id1_1_0_, ..... from USERS userunidir0_ where userunidir0_.USER_ID=?
[Hibernate] select userunidir0_.USER_ID as user_id1_1_0_, ..... from USERS userunidir0_ where userunidir0_.USER_ID=?
[Hibernate] select userunidir0_.USER_ID as user_id1_1_0_, ..... from USERS userunidir0_ where userunidir0_.USER_ID=?
[Hibernate] select userunidir0_.USER_ID as user_id1_1_0_, ..... from USERS userunidir0_ where userunidir0_.USER_ID=?

Wida膰, 偶e dla ka偶dego z rekord贸w zosta艂o wys艂ane dodatkowe zapytanie – w sumie 6 zapyta艅. Zapytania zosta艂y wygenerowane automatycznie przez Hibernate w ramach mapowania danych.

Psst… Interesuj膮cy artyku艂?

Je偶eli podoba Ci si臋 ten artyku艂 i chcesz takich wi臋cej – do艂膮cz do newslettera. Nie omin膮 Ci臋 materia艂y tego typu.

.

Problem N+1 dla relacji typu LAZY

Geneza podobna jest do sytuacji z relacj膮 jednokierunkow膮 typu EAGER. R贸偶nica polega na tym, 偶e dodatkowe zapytanie zostaje wygenerowane dopiero w momencie pr贸by odczytu danych z powi膮zanej encji (innych ni偶 klucz g艂贸wny) o ile nie istnieje w kontek艣cie persystencji. Jest to opisane wy偶ej.

Poprawa wydajno艣ci poprzez usuni臋cie problemu N+1

Zak艂adaj膮c, 偶e w danej logice potrzebujesz danych z obydwu tabel, to masz nast臋puj膮ce sposoby na to, aby unikn膮膰 N+1:

  1. Je偶eli powi膮zane encje dla relacji @ManyToOne lub @OneToOne znajd膮 si臋 w cache (kontekst persystencji) to dodatkowe zapytania nie zostan膮 wykonane. Mo偶esz to zrobi膰 np. poprzez wcze艣niejsze wyszukanie rekord贸w z drugiej tabeli – osobne query. Zostanie wykonane osobne zapytanie SQL, a zmapowany wynik zasili cache.
  2. Poprzez dwukierunkowo艣膰 relacji (dwie strony typu EAGER) i skorzystanie ze z艂膮czenia – wyszukujemy nie karty, ale u偶ytkownik贸w, kt贸rzy maj膮 karty dla kraju ’EA'.

Poprawa wydajno艣ci – operacje masowe

Korzystanie z automatycznej propagacji aktualizacji zmian w encjach jest wygodne. Nie musisz si臋 przejmowa膰 wywo艂ywaniem metody save lub update. Po prostu dzia艂a – metoda flush wykryje wszystkie Twoje zmiany i prze艣le do bazy danych – jedna po drugiej. I istotne jest w艂a艣nie to jedna po drugiej.

Wyobra藕 sobie sytuacj臋 ludzi robi膮cych zakupy. Jest kolejka, ka偶dy chce co艣 kupi膰, chodzi po sklepie, czeka w kolejce do kasy. Wydaje si臋 to naturalne i prawid艂owe. Ale za艂贸偶my, 偶e to Ty chcesz kupi膰 jaki艣 towar.

Potrzebny towar
Potrzebny towar

I potrzebujesz wielu sztuk.

Potrzebny towar w du偶ej ilo艣ci
Potrzebny towar w du偶ej ilo艣ci

Nie p贸jdziesz do sklepu po jedn膮 sztuk臋, a raczej kupisz od razu kilka. Zaoszcz臋dzisz czas (droga tam i z powrotem, kolejka, p艂atno艣膰).

Podobnie jest z wymian膮 danych pomi臋dzy Hibernate a baz膮 danych. Zapytanie musi zosta膰 wys艂ane do bazy i skompilowane a zwr贸cony wynik przetworzony przez Hibernate. Musz膮 zosta膰 zwolnione zasoby.

Uproszczony proces wysy艂ki instrukcji SQL i odebrania wyniku przez Hibernate
Uproszczony proces wysy艂ki instrukcji SQL i odebrania wyniku przez Hibernate

Ale zamiast wykonywa膰 te operacje po kolei, mo偶na je wykona膰 masowo. Zaoszcz臋dzisz czas podobnie jak w przypadku zakup贸w wielu sztuk jaj. Mo偶esz skorzysta膰 z kilku mechanizm贸w, kt贸re zapewni膮 wykonywanie operacji w grupach:

Update i delete z poziomu JPQL/HQL

Zapytania UPDATE i DELETE dotykaj膮ce wielu element贸w mo偶na wykorzysta膰 w przypadku, kiedy masz wsp贸lny mianownik dla encji – na przyk艂ad wszystkie karty u偶ytkownika. Przyk艂ad: usuwanie wszystkich kart danego u偶ytkownika. Kod Java:

var query = session.createQuery("DELETE FROM Card where cardOwner=:ownerId");
query.setParameter("ownerId", ownerId);
query.executeUpdate();

Nale偶y pami臋ta膰 o tym, i偶 zapytania nie aktualizuj膮 encji w cache.

Update i delete z poziomu Criteria API

Wykorzystanie Criteria API dla Hibernate ma zastosowanie podobne jak w punkcie wy偶ej – tak偶e nie aktualizuje encji w cache. Przyk艂ad analogiczny jak wy偶ej, kod Java:

public void deleteUserCardsCriteria(String ownerId) {        
    //Tworzymy Builder
    var criteriaBuilder = session.getCriteriaBuilder();
    //CriteriaDelete dla klasy Card
    var criteriaDelete = criteriaBuilder.createCriteriaDelete(Card.class);
    //Fraza FROM
    var delete = criteriaDelete.from(Card.class);
    //Fraza Where
    criteriaDelete.where(criteriaBuilder.equal(delete.get("cardOwner"), ownerId));
    //Wykonanie operacji
    session.createQuery(criteriaDelete).executeUpdate();        
}

Grupowanie operacji w batch

Grupowanie operacji batch dzia艂a inaczej ni偶 z wykorzystaniem QL czy Criteria.

  • Korzystasz z API Hibernate w „klasyczny” spos贸b – zmieniasz warto艣ci p贸l encji, wykonujesz operacje persist czy delete dla Session/EntityManager.
  • Mo偶esz aktualizowa膰 dane encji, usuwa膰 czy dodawa膰 encje w dowolnej kolejno艣ci.
  • Operacje batch bazuj膮 na danych encji znajduj膮cych si臋 w kontek艣cie persystencji (PersistenceContext czyli cache’u 1-go poziomu).
  • Operacje zostan膮 pogrupowane wg typu obiektu i typu operacji (INSERT/UPDATE/DELETE).

Aby skorzysta膰 z tej w艂a艣ciwo艣ci, nale偶y ustawi膰 property hibernate.jdbc.batch_size. Warto艣膰 to liczba operacji, po kt贸rych nast膮pi wysy艂ka zebranych operacji do bazy danych.

Przyk艂ad:

  • Wyszukujemy w zbiorze kart, te, kt贸re nale偶膮 do danego u偶ytkownika.
  • Dla ka偶dej znalezionej karty deaktywujemy j膮 (atrybut enabled).
  • Ustawiamy warto艣膰 hibernate.jdbc.batch_size na 5.

Wygenerowany log – wida膰 zapytanie SELECT, nast臋pnie 5 razy UPDATE i wys艂anie polecenia batchowego Executing batch size: 5

[2020-12-16 07:51:05,506] [DEBUG] [o.h.SQL SqlStatementLogger.java:144] select card0_.CARD_ID as card_id1_0_, card0_.COUNTRY as country2_0_, card0_.CARD_OWNER as card_own3_0_, card0_.CARD_UUID as card_uui4_0_, card0_.ENABLED as enabled5_0_ from CARDS card0_ where card0_.ENABLED='Y' limit ?
[2020-12-16 07:51:05,535] [DEBUG] [o.h.SQL SqlStatementLogger.java:144] update CARDS set ENABLED=? where CARD_ID=?
[2020-12-16 07:51:05,537] [DEBUG] [o.h.SQL SqlStatementLogger.java:144] update CARDS set ENABLED=? where CARD_ID=?
[2020-12-16 07:51:05,537] [DEBUG] [o.h.SQL SqlStatementLogger.java:144] update CARDS set ENABLED=? where CARD_ID=?
[2020-12-16 07:51:05,537] [DEBUG] [o.h.SQL SqlStatementLogger.java:144] update CARDS set ENABLED=? where CARD_ID=?
[2020-12-16 07:51:05,537] [DEBUG] [o.h.SQL SqlStatementLogger.java:144] update CARDS set ENABLED=? where CARD_ID=?
[2020-12-16 07:51:05,538] [DEBUG] [o.h.e.j.b.i.BatchingBatch DelegatingBasicLogger.java:384] Executing batch size: 5

Pami臋taj! Ustawienie warto艣ci hibernate.jdbc.batch_size wcale nie oznacza, 偶e operacje zostan膮 wys艂ane dok艂adnie po osi膮gni臋ciu ustawionego limitu – mo偶e si臋 to sta膰 wcze艣niej np. przed wywo艂aniem innego zapytania.

Diagnostyka – 艣ledzenie zapyta艅 SQL

Hibernate podczas wykonywania operacji zwi膮zanych z komunikacj膮 pomi臋dzy Java a baz膮 danych loguje sporo informacji. W zale偶no艣ci od w艂膮czonego poziomu logowania w obszarze dotycz膮cym zapyta艅 SQL zobaczysz mi臋dzy innymi:

  • Tre艣膰 instrukcji SQL.
  • Warto艣ci parametr贸w.
  • Informacje o przetwarzaniu wsadowym/batchowym.

Maj膮c wiedz臋, jak wygl膮da wysy艂ane zapytanie SQL, b臋dziesz w stanie oceni膰 czy jest ono optymalne.

艢ledzenie zapyta艅 SQL
艢ledzenie zapyta艅 SQL

Opr贸cz klasycznego logowania Hibernate dostarcza dodatkowego wydruku polece艅 SQL, kt贸re sterowane jest poprzez properties w Hibernate.

!!! Ale uwa偶aj !!! – w艂膮czenie zbyt szerokiego poziomu logowania mo偶e spowodowa膰, 偶e wydajno艣膰 spadnie jeszcze bardziej, a system b臋dzie generowa艂 setki MB log贸w.

Konfiguracja 艣ledzenia SQL poprzez properties Hibernate

W ramach properties znanych Hibernate:

  • hibernate.show_sql – true/false – wy艣wietli tre艣膰 zapytania SQL,
  • hibernate.format_sql – true/false – sformatuje tre艣膰 wy艣wietlanego zapytania SQL.

Mo偶esz dodatkowo w艂膮czy膰 kolorowanie sk艂adni wy艣wietlanego SQL

  • hibernate.highlight_sql – true/false – pokoloruje tre艣膰 wy艣wietlanego zapytania SQL w konsoli.

Tak b臋dzie wygl膮da艂o wy艣wietlone i pokolorowane zapytanie

[Hibernate] 
    select
        card0_.CARD_ID as card_id1_0_,
        card0_.COUNTRY as country2_0_,
        card0_.CARD_OWNER as card_own3_0_,
        card0_.CARD_UUID as card_uui4_0_,
        card0_.ENABLED as enabled5_0_ 
    from
        CARDS card0_ 
    where
        card0_.ENABLED='Y' limit ? offset ?

Warto艣ci powy偶szych properties ustawi膰 mo偶na w plikach konfiguracyjnych Hibernate.

Przyk艂ad dla hibernate.properties

hibernate.show_sql=true
hibernate.format_sql=true
hibernate.highlight_sql=true

Przyk艂ad dla hibernate.cfg.xml

<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE hibernate-configuration SYSTEM
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <!-- -->
        <property name="hibernate.show_sql">true</property>
        <property name="hibernate.format_sql">true</property>
        <property name="hibernate.highlight_sql">true</property>
        <!-- -->
    </session-factory>
</hibernate-configuration>

Konfiguracja 艣ledzenia SQL poprzez framework do logowania

Aby w艂膮czy膰 艣ledzenie polece艅 SQL w klasycznym logu Hibernate, powiniene艣 w艂膮czy膰 odpowiedni poziom logowania dla specyficznych klas. Te klasy to:

  1. org.hibernate.SQL
    • poziom DEBUG
    • wy艣wietla tre艣膰 zapytania SQL
    • przyk艂ad logu
[2020-12-15 21:52:39,619] [DEBUG] [o.h.SQL SqlStatementLogger.java:144] 
    update
        CARDS 
    set
        ENABLED=? 
    where
        CARD_ID=?
  1. org.hibernate.type.descriptor.sql.BasicBinder
    • poziom TRACE
    • wy艣wietla warto艣ci parametr贸w
    • przyk艂ad logu
[2020-12-15 21:52:39,618] [TRACE] [o.h.t.d.s.BasicBinder BasicBinder.java:64] binding parameter [1] as [VARCHAR] - [N]
[2020-12-15 21:52:39,618] [TRACE] [o.h.t.d.s.BasicBinder BasicBinder.java:64] binding parameter [2] as [VARCHAR] - [4]
  1. org.hibernate.engine.jdbc.batch.internal.BatchingBatch
    • poziom DEBUG
    • wy艣wietla informacj臋 o tym, 偶e zosta艂y wywo艂ane polecenia wsadowe
    • przyk艂ad logu
[2020-12-15 21:52:39,621] [DEBUG] [o.h.e.j.b.i.BatchingBatch DelegatingBasicLogger.java:384] Executing batch size: 5

Konfiguracja dla logback – logback.xml

<configuration>
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>[%date] [%level] [%logger{10} %file:%line] %msg%n</pattern>
        </encoder>
    </appender>
    <logger name="org.hibernate.SQL" level="TRACE" additivity="false">
        <appender-ref ref="CONSOLE"/>
    </logger>
    <logger name="org.hibernate.type.descriptor.sql.BasicBinder" level="TRACE" additivity="false">
        <appender-ref ref="CONSOLE"/>
    </logger>
    <logger name="org.hibernate.engine.jdbc.batch.internal.BatchingBatch" level="TRACE" additivity="false">
        <appender-ref ref="CONSOLE"/>
    </logger>

    <root level="info">
        <appender-ref ref="CONSOLE"/>
    </root>
</configuration>

Konfiguracja logowania w Spring – poprzez application.properties

logging.level.org.hibernate.SQL=TRACE
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE

Poprawa wydajno艣ci – pobieraj tylko to czego potrzebujesz

Logika aplikacji operuj膮ca na danych z okre艣lonych tabel mo偶e by膰 r贸偶na, w zale偶no艣ci od funkcjonalno艣ci aplikacji.

  1. Czasami nie potrzebujesz doci膮ga膰 wszystkich powi膮zanych obiekt贸w, 偶eby dowiedzie膰 si臋, 偶e istniej膮 powi膮zane dane. Przyk艂ad chcesz wy艣wietli膰 list臋 z danymi u偶ytkownik贸w i informacj膮 o liczbie kart. Nie musisz pobiera膰 listy kart, aby tylko pobra膰 rozmiar.
    • Mo偶esz wykorzysta膰 pole wyliczalne w klasie – anotacja @Formula. Przyk艂ad dla tabeli USERS, encja User.
 @Formula("(SELECT COUNT(1) FROM CARDS C WHERE C.CARD_OWNER_ID=USER_ID)")
    private int cardsCount;

Psst… Interesuj膮cy artyku艂?

Je偶eli podoba Ci si臋 ten artyku艂 i chcesz takich wi臋cej – do艂膮cz do newslettera. Nie omin膮 Ci臋 materia艂y tego typu.

.

  1. Mo偶esz skorzysta膰 z projekcji. Projekcje to r贸偶ne zachowanie i wygl膮d danych dla tej samej tabeli. Hibernate na to pozwala – mo偶esz zdefiniowa膰 wiele encji na tej samej tabeli i mie膰 inny zbi贸r p贸l dla wyszukiwania, a inny dla wej艣cia w szczeg贸艂y. Ale zaleca si臋, aby modyfikacje i dodawanie by艂y jedynie tam, gdzie masz dost臋p do szczeg贸艂贸w. Mo偶esz zabezpieczy膰 si臋 przed przypadkow膮 modyfikacj膮, na przyk艂ad poprzez:
    • U偶ycie anotacji org.hibernate.annotations.Immutable.
    • Usuni臋cie/ukrycie (private) metod typu set dla p贸l encji oraz zezwolenie jedynie na operacje wyszukiwania z poziomu DAO/Repository.
    • Dodaj metod臋 rzucaj膮c膮 wyj膮tek, kt贸r膮 oznacz anotacjami @PrePersist, @PreUpdate, @PreRemove. W ten spos贸b dodatkowo si臋 zabezpieczysz przed zmianami.
  2. Widoki bazodanowe (CREATE VIEW AS SELECT) to nazwana definicja jakiego艣 zapytania typu SELECT. Hibernate umo偶liwia stworzenie encji opartej o widok. Oznaczenie encji dla widoku nie r贸偶ni si臋 to niczym od encji dla tabeli.
  3. Anotacja @EntityGraph umo偶liwia w艂asne sterowanie doczytywaniem dla relacji
@Entity
@Table(name = "CARDS")
@Data
@AllArgsConstructor
@NoArgsConstructor
@NamedEntityGraphs({
        @NamedEntityGraph(name = "softwareskill-no-relations",
                attributeNodes = {
                        @NamedAttributeNode("cardId"),
                        @NamedAttributeNode("cardUuid"),
                        @NamedAttributeNode("cardCountry")
                }
        ),
        @NamedEntityGraph(name = "softwareskill-all-relations",
                attributeNodes = {
                        @NamedAttributeNode("cardId"),
                        @NamedAttributeNode("cardUuid"),
                        @NamedAttributeNode("cardCountry"),
                        @NamedAttributeNode("cardOwner")
                }
        )
})
public class CardByEntityGraph {
    @Id
    @Column(name = "CARD_ID")
    private String cardId;

    @Column(name = "CARD_UUID")
    private String cardUuid;

    @Column(name = "COUNTRY")
    @Enumerated(EnumType.STRING)
    private CardCountry cardCountry;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "CARD_OWNER_ID")
    private UserUnidirectional cardOwner;
}

W powy偶szym przyk艂adzie masz zdefiniowane dwa rodzaje graf贸w doci膮gania danych:

  • softwareskill-no-relations – bez doci膮gania danych o relacjach (nawet je艣li jest EAGER nad polem). Przyk艂ad wywo艂ania poni偶ej
public Optional<CardByEntityGraph> findByIdNoRelations(String cardId) {
    var entityGraph = session.getEntityGraph("softwareskill-no-relations");
    Map<String, Object> properties = new HashMap<>();
    properties.put("javax.persistence.fetchgraph", entityGraph);
    var value = session.find(CardByEntityGraph.class, cardId, properties);
    return Optional.ofNullable(value);
}
  • softwareskill-all-relations – z doci膮gni臋ciem relacji

Wpis kt贸ry czytasz to zaledwie fragment wiedzy zawartej w Programie szkoleniowym Java Developera od SoftwareSkill. Mamy do przekazania sporo usystematyzowanej wiedzy z zakresu kluczowych kompetencji i umiej臋tno艣ci Java Developera. Program sk艂ada si臋 z kilku modu艂贸w w cotygodniowych dawkach wiedzy w formie video.

Wydajno艣膰 Hibernate

Tw贸rz szybko dzia艂aj膮ce aplikacje z wydajn膮 i zoptymalizowan膮 obs艂ug膮 bazy danych.

Podsumowanie

Rozwi膮zanie jest zale偶ne od potrzeb danej sytuacji. Zale偶y, z czym si臋 mierzysz i co mo偶e by膰 problemem. Ile jest tabel, jakiego rodzaju s膮 kolumny (np. bardzo du偶e), jaka jest ich liczba. Przedstawi艂em Ci kilka aspekt贸w dotycz膮cych problem贸w z wydajno艣ci膮 i pokaza艂em, w jaki spos贸b mo偶esz rozwi膮za膰 dany problem. Ale najwa偶niejsze jest to, aby艣 wiedzia艂 jak zachowuje si臋 framework Hibernate i potrafi艂 diagnozowa膰 i 艣ledzi膰 wymian臋 danych z baz膮.

Gdyby艣 potrzebowa艂 jeszcze wi臋cej:

Jeste艣 Java Developerem?

Przejd藕 na wy偶szy poziom wiedzy 
„Droga do Seniora” 馃敟馃挭

Jeste艣 Team Leaderem? Masz zesp贸艂?

Podnie艣 efektywno艣膰 i wiedz臋 swojego zespo艂u 馃憣

Linki

Obrazy:

Dyskusja