Ju偶 dost臋pny

Program Szkoleniowy Java Developer dost臋pny 馃敟馃挭 tylko TERAZ za 1299 z艂Sprawd藕 szczeg贸艂y i agend臋

Zakres

Monitoring 鈥 Apache Kafka 鈥 Clean Code Testowanie 鈥 Hibernate 鈥 Systemy kolejkowe Sprawd藕 szczeg贸艂y i agend臋

Zakres

14 modu艂贸w聽 /聽 ponad 40h nagra艅聽 /聽 230 lekcji聽 /聽 do偶ywotni dost臋p聽 /聽 Sprawd藕 szczeg贸艂y i agend臋

Sposoby na popraw臋 wydajno艣ci Hibernate 馃殌

utworzone przez 3 stycznia 2021Hibernate, Java, Tip and Tricks

[Szybkie info]: Startujemy z IV edycj膮 Programu Szkoleniowego Java Developera 馃殌. To MEGA pigu艂a wiedzy o Java 馃敟馃挭

  • 14 tygodniowy program szkoleniowy online,
  • 230 lekcji w formie video (40 godzin materia艂u)
  • z do偶ywotnim dost臋pem
  • Case Studies, masz dost臋p do kodu i obraz贸w Dockerowych
  • zamkni臋ta grupa mentorzy + uczestnicy i webinary na 偶ywo

W agendzie znajdziesz: Mikroserwisy, Systemy kolejnowe, Apache Kafka, Caching, Hibernate/MyBatis/Spring Data, techniki efektywnych Test贸w kodu, Clean Code i Maven.

Tylko teraz do艂膮czysz z 50% rabatem to 2699 z艂 1299 z艂 (+VAT). I nigdy ju偶 nie b臋dzie taniej. Poni偶ej dowiesz si臋 wi臋cej:

Zobacz wi臋cej

A teraz przechodzimy do artyku艂u:

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