Skip to content

Czy Hibernate w 2021 to najlepszy wybór?

Hibernate dekadę temu

Mamy rok 2009, jestem studentem i w trakcie przedmiotu o nowoczesnych aplikacjach internetowych, po raz pierwszy słyszę o Hibernate. Jestem niesamowicie podekscytowany, bo wizja tego, aby zapomnieć o SQL i móc dostawać się do danych przy użyciu czystej Javy brzmi bardzo atrakcyjnie. Ok, gdzieś tam w tle jest jeszcze ten paskudny xml z konfiguracją, ale da się to przełknąć.

Sam SQL wydaje mi się wtedy mało przyjemny, ale jeszcze nie rozumiem, że to głównie za sprawą nie najlepszego doktora, który przez sposób prowadzenia zajęć obrzydził większości ten język. Dokładnie z tego samego powodu 10 lat wcześniej wręcz nienawidziłem języka niemieckiego. Dziś SQL uwielbiam, zaś co do niemieckiego, język odczarował mi serial Dark i niektóre z utworów Rammsteina 🙂

Dobra, to nie jest post o użalaniu się nad stanem polskiej edukacji i ludzi z łapanki w roli nauczycieli. Wróćmy zatem do naszego głównego bohatera Hibernate. Ochoczo rzucam się w wir pisania pierwszych prostych aplikacji i jestem oczarowany możliwościami łączenia dwóch światów obiektowości i tabel. Wszystko do momentu, w którym moja aplikacja jest bardziej rozbudowanym projektem studenckim. Czyli w praktyce przekłada się to na coś więcej niż prosty CRUD z jedną tabelą. Nagle pojawiają się pierwsze schody…

Te schody tutaj to tak naprawdę ruiny tarasów rolnych starożytnej cywilizacji Inków w Peru. Zastanawiam się, czy ktoś podobnie podchodzi do perstistence.xml w starych projektach 🙂

Nagle okazuje się, że aby zrozumieć pewne nieoczekiwane zachowania, trzeba poświęcić na to trochę czasu. Jak po latach się przekonam, najlepiej poświęcić na to tak kilka lat :-). Tym sposobem z prostego narzędzia, które obiecywało wiele przy małym nakładzie pracy, wyłania się skomplikowany i trudny w obsłudze kombajn. Z czasem na moich oczach upada też obietnica, o tym, że już nigdy nie będę musiał wracać do SQL. Nic bardziej mylnego, aplikacje krztuszą się, działają wolno, a w logach pojawia się cała masa zapytań, więc trzeba optymalizować.

Kto pamięta bizony? Akurat ten przykład kombajnu, może być nie do końca trafiony jako metafora. Maszyny te do dziś cieszą się niezawodnością, łatwo dostępnymi częściami zamiennymi, dzięki czemu mogą konkurować z droższymi zachodnimi rozwiązaniami.

Projekty studenckie rządzą się swoimi prawami. Dlatego też moje pierwsze zetknięcie się z Hibernatem, nie kończy się większą katastrofą. Na taką przyjdzie poczekać kilka lat, gdy już za swoją pracę będę dostawał już wynagrodzenie. Ostatecznie udaje mi się to doprowadzić do stanu nieidealnego działania (to takie działanie, które jako tako działa, ale strach dotknąć, żeby czegoś nie zepsuć), zaliczyć przedmiot i pójść dalej, z nadzieją, że to tylko kwestia nauczenia się kilku rzeczy, których teraz nie jestem jeszcze świadom. Świat nie może się w końcu mylić i nie bez powodu framework ten jest uznawany za standard… Nie może, prawda?

Hibernate dziś

Mamy rok 2021, w ogłoszeniach o pracę i kodach źródłowych wielu aplikacji nadal króluje Hibernate. Jest to też jeden z popularniejszych frameworków, który jest nauczany w szkołach programowania zaraz po Javie i Springu. Narzędzia nieco dojrzały, nadbudowały dodatkową warstwę abstrakcji, tym razem obiecując, że nie musisz w pełni znać Hibernate, bo jest przecież Spring DATA, który załatwi za Ciebie wszystko. Konfigurację XML’a dziś rozsmarowywujemy niczym Delmę po wszystkich klasach, które reprezentują tabele naszej bazy (metafora do margaryny nie jest tutaj przypadkowa) Problem w tym, że ja nadal mam wrażenie, że te schody z poprzedniego rozdziału nadal istnieją, tylko wiosna (eng. Spring) przykryła je zielonym mchem i są nieco bardziej miękkie. Poza tym są dokładnie tak samo wysokie jak ponad dekadę temu.

Jeden z wykładów drugiego sezonu Javafaktury, który świetnie tłumaczy, czemu korzystając z Spring DATA nie da się uciec od znajomości Hibernate.

Po co ten wpis?

No właśnie Tomek, po co ten wpis? Już spieszę z odpowiedzią. W ramach tego wpisu obrałem sobie trzy cele:

  1. Podzielić się przemyśleniami, że Hibernate to naprawdę złożony framework, trudny do opanowania. Potwierdza to zresztą wiele autorytetów jak Martin Fowler w swoim pamiętnym wpisie ORM Hate
  2. Pokazać, że pomimo pierwszego punktu są sposoby, aby łatwiej radzić sobie z nim, a wiedza z tego zakresu jest w sieci całkiem dobrze udokumentowana, z jednym wyróżniającym się źródłem, które warto abyś poznał
  3. Zachęcić do spróbowania alternatywnych rozwiązań, które mogą sprawdzić się o wiele lepiej i jaka jest ich cecha wspólna.

No to zaczynamy!

Hibernate jest trudny!

Oprócz samej złożoności tego frameworka, która jest podstawą wielu problemów, wiele przykrych rzeczy, których doświadczysz, wynika ze złej organizacji twojego kodu. Wiele takich decyzji projektowych powoduje lawinowy wzrost złożoności i tak już trudnego narzędzia i osobom z małym doświadczeniem, bardzo ciężko jest to okiełznać, skupiając się tylko i wyłącznie na samej konfiguracji. W pewnych momentach wygląda to prawie jak walka z Hydrą z odrastającymi głowami. Tam, gdzie „niechcący” wyciągasz pół bazy, bo zastosowałeś Fetch.EAGER za chwilę po poprawce, okazuje się, że w innym miejscu masz N+1 i problem z nadmiarem zapytań. I racja, pomóc może tutaj JPA Entity Graph, o którym dowiesz się, jeżeli tylko odpowiednio długo będziesz poznawał kolejne meandry Hibernate’a. Problem jednak w tym, że to tylko dokłada kolejną cegiełkę i udowadnia tezę postawioną w tym akapicie.

Jeżeli masz taki problem, że te same encje bazodanowe wykorzystujesz w różnych kontekstach i chciałbyś dynamicznie sterować tym, co jest w ramach nich wyciągane, pomoże Ci JPA Entity Graph, o którym wprowadzenie znajdziesz np. tutaj: https://www.baeldung.com/jpa-entity-graph

Obietnica, na którą łapie się zbyt wielu początkujących

Na początku wszystko wydaje się niesamowicie proste. W roku 2021 zakrawa wręcz o prawdziwą magię. Kilka adnotacji, interfejs repozytorium i nasz CRUD jest już gotowy. Zestawiając to z klepaniem zapytań z palucha, nawiązywaniem połączenia, później zrzucaniem tego do obiektów, wniosek, jaki wyłania się z tego, jest prosty. Hibernate to narzędzie idealne.

Nieoczekiwane zachowania twojej aplikacji

Nic nie jest jednak za darmo i dług zaciągnięty na początku jako początkujący programista, będziesz musiał spłacić później. Wraz, ze wzrostem złożoności twojej bazy, rośnie też złożoność konfiguracji, którą będziemy chcieli ją okiełznać. Z takich szybkich znanych problemów, na jakie napotykają młodzi adepci programowania:
– a to baza wyciąga za dużo danych,
– a to za mało, co skutkuje N+1,
– a to zapisuje tylko cześć danych i gubi jakieś po drodze,
– a to aplikacja nie startuje, bo coś wysypuje ją na starcie w runtime,
….

Szereg tajemniczych wyjątków

Oprócz problemów poznajesz specyficzne dla tego framewowka wyjątki:
org.hibernate.StaleObjectStateException – rzucany w momencie próby jednoczesnego zapisywania zmian przez dwa wątki,
org.hibernate.LazyInitializationException – rzucany, gdy próbujesz dostać się do danych poza sesją Hibernate’ową
org.hibernate.exception.ConstraintViolationException – przy próbie zapisu zduplikowanych danych
org.hibernate.TransientObjectException – podczas próby zapisu grafu obiektów, bez ustawionej opcji Cascade

Wydajność, a raczej jej brak

Jakby tego było mało, miejscami działa wolno, generuje masę zapytań pod spodem. Ciężko się temu dziwić w końcu konfiguracja to nierzadko zestaw adnotacji przyklejonych prosto ze stackoverflow, których do końca nie rozumiesz.

To nieprawda, że nie ma rozwiązań typu silver bullet. Hibernate, zdecydowanie do takich należy i może zabić twoją aplikację na amen! 🙂

Wróćmy do podstaw

Niektóre z powyższych problemów oraz inne tylko pozornie łatwe pułapki omówił Kuba Kubryński na Confiturze 2016.

To video już było na łamach mojego bloga, ale nie sposób nie wspomnieć o nim jeszcze raz przy takiej okazji. Co najciekawsze, nawet osoby twierdzące, że znają Hibernate od wielu lat, podczas prezentacji na żywo, przyznawały, że dowiedziały się czegoś nowego.

Jak ułatwisz sobie życie z Hibernatem

Zdaję sobie sprawę, że są osoby, które z powodzeniem potrafią wykorzystywać go, realizując swoje cele biznesowo-projektowe. Przedstawię Ci kilka porad, które powtarzają się u tych osób jak dobre praktyki, których używają. Na koniec tego rozdziału przybliżę Ci też pewną postać, która w przeciwieństwie do mnie, może pomóc Ci w twoich codziennych problemach z tym frameworkiem i go nieco odczarować.

Zdecydowanie da się łatwiej żyć z Hibernatem i odpowie tak każdy pasjonat tej technologii lub osoba świadomie decydująca się na wybór Hibernate w swoim projekcie. Niewątpliwie każda technologia właściwie użyta spełni się dobrze w zadaniu. Użycie Hibernate’a okaże się dużo prostsze, gdy skorzystasz z kilku poniższych porad:

Czy aby na pewno potrzebujesz frameworka ORM?

Na początek, pytanie fundamentalne, kiedy Hibernate (lub inny ORM) ma największe szanse na sukces i skuteczne wykorzystanie. Poniżej przedstaiwam Ci prosty algorytm, odpowiadający na pytanie, czy rozwiązanie pokroju Hibernate może Ci w czymś pomóc, wygląda następująco:

Używać czy nie używać?

Z wykresu możemy wyczytać, kilka wniosków:
– Po pierwsze Hibernate sprawdzi się wszędzie tam, gdzie mamy do czynienia z małymi wymaganiami wydajnościowymi, a dużą złożonością modelu, na którym operujemy.
– Hibernate nie ma najmniejszego sensu, przy wysokim ruchu i prostym modelu danych
– Reszta obszarów to obszary, które można rozważyć z zastrzeżeniem, aby uwzględnić w tych rozważaniach swoje doświadczenie, jakie posiadasz.

Zacznij od bazy, a nie od klas!

Pomocny może okazać się Flyway lub inne narzędzie służące do wersjonowania schematu bazy i trzymania tego razem z kodem aplikacji. To element, który w początkowym stadium niewiele osób wdraża w swoim projekcie. Mimo że Hibernate, dostarcza ustawienia pozwalające na definiowanie, w jaki sposób ma się obchodzić z naszym schematem, to większość doświadczonych programistów doradza, aby nie korzystać z opcji update na produkcji. Głównym powodem jest to, że opcja ta nie do końca radzi sobie ze wszystkimi zmianami i że schemat bazy jest ważniejszy od reprezentacji encji w kodzie, dlatego to on powinien być generowany pierwszy.

Pomimo że istnieje pokusa, aby zarządzenie schematem bazy pozostawić frameworkowi, najczęściej kończy się to długiem, który prędzej lub później będziemy musieli spłacić. Jest świetny artykuł podsumowujący ten temat https://blog.jooq.org/2018/06/06/truth-first-or-why-you-should-mostly-implement-database-first-designs/

Rozsądnie krój aplikację na poziomie domen stosując praktyki DDD

Powyższa rada pozwoli Ci okiełznać złożoność kodu. Jeżeli wydzielisz w swojej aplikacji agregaty, które będą strzegły dostępu do niżej ulokowanych elementów, mocno zmniejszysz coupling swoich klas. Jako przykład posłużę się prostym przykładem modelowania domeny bloga, który właśnie czytasz. Z pewnością zauważyłeś, że sercem każdego bloga są wpisy autora oraz komentarze społeczności. Na prostym diagramie obrazującym tę zależność będzie to wyglądać mniej więcej tak:

Przypominam, że to uproszczony model, niezakładający wątków w komentarzach i paru innych elementów komplikujących tę domenę.

Przy okazji jak już jesteśmy przy wątku komentarzy. Jeżeli jeszcze nie miałeś okazji skomentować żadnego mojego artykułu, poświęć chwilę i zrób to, będzie mi bardzo miło. Da mi też to sygnał, czy to, co robię, ma sens i daje Ci jakąkolwiek wartość 🙏

Od strony kodu, ten fragment da się obsłużyć, realizując warstwę DAO, osobną dla komentarzy i osobną dla wpisów na blogu. Z mojego doświadczenia pracy z osobami początkującymi, to najczęstszy kierunek, jaki obierają lub jest im doradzany. Sprowadza się to do stworzenia dedykowanych CRUD’ów zarówno dla komentarzy jak i samych wpisów. W efekcie mamy więc 2 osobne ścieżki zarządzające tymi bytami.

Przez powyższy przykład chce pokazać, że czasem zapędy w krojeniu i wydzielaniu osobnych bytów idą za daleko i w praktyce będą wymagały dużo powiązań na poziomie kodu. Powiązań zupełnie niepotrzebnych.

Przy większej ilości tabel (dla osób początkujących, wystarczy dosłownie kilka) takie podejście, mocno skomplikuje nam kod i spowoduje, że ciężko będzie nim zarządzać. Dzieje się tak dlatego, że te byty prawie nigdy nie występują w izolacji jak zakłada to na początku CRUD i a to, będziemy modyfikować kilka informacji naraz, a to odczytać kilka informacji z różnych źródeł w jednym miejscu.

Dlatego dużo lepszym rozwiązaniem może się okazać się, podejście do tego w nieco inny sposób. Po pierwsze zastanów się nad kilkoma założeniami, z prostego przykładu bloga:
– komentarze, zawsze są dociągane razem z informacją o danym wpisie i stanowią całość,
– wszelkie operacje na komentarzu odbywają się zawsze w kontekście wpisu (pojęcie korzenia agregatu),
– komentarze, nie są osobnym bytem, który rozpatrywany jest w innym kontekście niż kontekst wpisu na blogu

Powyższe założenia pozwolą Ci zrezygnować z całej odrębnej ścieżki związanej z komentarzami, a niezbędne funkcjonalności związane z nimi wkomponować w miejscu zarządzania wpisami.

W praktyce powoduje to, że np. nie masz już duplikowanych ścieżek CRUD dla każdej tabeli powyżej warstwy dostępu do danych. Posiadasz wtedy serwis obsługujący wpisy na blogu i zarządzający również komentarzami w kontekście danego wpisu. Nie ma możliwości dostania się do pojedynczego komentarza, bez zidentyfikowania, z jakim wpisem jest on powiązany. A wszelkie operacje na komentarzach pozostają pod nadzorem naszego agregatu, czyli w tym wypadku jednego wpisu na blogu.

Dobrym przykładem na, który trafiłem w sieci omawiającym powyższy pomysł i rozszerzający ten punkt widzenia jest artykuł pokazujący mały wycinek domeny systemu zgłoszeń https://blog.pragmatists.com/refactoring-from-anemic-model-to-ddd-880d3dd3d45f.

Oczywiście to wszystko zależy od naszych wymagań. Może okazać się, że komentarze, np. w drodze ewolucji mogą się okazać dużo bardziej skomplikowanym bytem. W którymś momencie na przykład będziemy chcieli w jakiejś formie móc prezentować wszystkie komentarze w jednym miejscu na naszym blogu np. sekcji „najnowsze komentarze”. W takim wypadku, powyższe podejście nie będzie już aktualne.

Odseparuj dostęp do danych od logiki twojej aplikacji

To bardzo dobra praktyka, która polega na modelowaniu logiki na czystym kodzie, bez użycia frameworków typu Spring i Hibernate, a dołączenie ich na późniejszym etapie, kiedy będziesz chciał przełączyć się na prawdziwe dane. Temat jest mocno powiązany z tematyką architektury hexagonalnej. Tak jak w pierwszym punkcie przytaczałem pojęcie „database first approach” tak tutaj idziemy jeszcze o krok dalej. Skupiamy się na logice i szybkim prototypowaniu założeń. Pomóc nam w tym może TDD, które bez całego bagaża ciężkich operacji I/O pozwoli skupić się na logice weryfikowanej przy pomocy błyskawicznych testów jednostkowych.

Hola, hola, ktoś zapyta, a co z danymi? Przecież sama logika, często mimo wszystko musi operować na prawdziwych danych?

Spokojnie, dane też będą tylko w lekkiej formie bez konieczności dołączania ciężkich zależności typu baza. Te najlepiej zamodelować jako lekkie źródło oparte na kolekcjach, hashmapie lub bazie pamięciowej. Ogromną zaletą takiego podejścia będzie mniejsza ilość mocków, które pojawiają się wszędzie tam, gdzie mamy do czynienia z zewnętrznymi komponentami typu baza, lub zbytnim splątaniem komponentów. Więcej na ten temat powie Jakub Nabrdalik.

O Kubie Nabrdaliku i innych fenomenalnych mówcach na polskiej scenie prelegentów mówiłem w tym wpisie. Jeżeli jeszcze nie widziałeś, zachęcam do lektury. https://kodujmy.pl/10-najlepszych-prelegentow-java-w-polsce-ktorych-warto-sledzic/

Znów się powtarzam, bo to video również gościło na ramach tego bloga, ale Kuba to czołówka jeżeli chodzi o Polską scenę programistyczno-prelegencką i każda minuta poświęcona na słuchanie, tego pana to rewelacyjnie zainwestowany czas.

Ta ostatnia rada, ma tę zaletę, że przy takim zamodelowaniu naszego kodu, Hibernate kiedyś w przyszłości, może zostać bardzo łatwo wymieniony na alternatywne rozwiązanie. Może to być podyktowane innego rodzaju źródłem danych (np. nierelacyjna baza danych, oferująca lepszą wydajność) lub prostszym bardziej intuicyjnym narzędziem.

Stosuj rozwiązania CRUD, tam gdzie ma to sens

Nie demonizuję CRUD’ów one nadal znajdą swoje miejsce w każdym projekcie, w szczególności tam, gdzie twoja domena jest mocno odchudzona i sprowadza się tylko i wyłącznie do tych 4 operacji. Wpinanie tam czegokolwiek bardziej rozbudowanego to przerost formy nad treścią. To na co powinieneś jednak uważać to zbyt pochopne ocenianie czy coś faktycznie jest CRUD’em, czy tylko początkowo się takowym wydaje.

Ważne, abyś zapamiętał, że powyższe rady nie tyczą się tylko i wyłącznie współpracy z tym konkretnym frameworkiem, a są zestawem ogólnych dobrych praktyk, które przydadzą się również w innych przypadkach.

A co z poradami dotyczącymi samego Hibernate’a?

Przede wszystkim poznaj Pana Vlada Mihalcea. To prawdziwa skarbnica wiedzy, jeżeli chodzi o ten framework. Vlad prowadzi jednego z popularniejszych blogów o tematyce około javowej w sieci, poświęconego głównie, właśnie naszemu antybohaterowi tego wpisu.

Vlad to również ceniony autor książki o wysoko wydajnościowym przechowywaniu danych w Javie oraz autor ciekawych bibliotek jak np. Hibernate-Types

Hypersistance Optimizer czyli pair programming z Vladem

Vlad jest też autorem rewelacyjnego narzędzia https://vladmihalcea.com/hypersistence-optimizer/. Narzędzie to konfigurujemy razem z naszym kodem. Od tej pory, dostajemy informację w postaci logów, mówiące nam, w których miejscach nasza konfiguracja JPA może zostać poprawiona i jak to zrobić. Vlad, gdzie byłeś 12 lat temu, kiedy pisałem aplikację, aby zaliczyć przedmiot!? 🙂

Jeden z komentarzy znalezionych na Twitterze szczególnie przypadł mi do gustu:

Używając Hypersistence Optimizera czujemy się jakbyśmy uprawiali pair-programming razem z Vladem

Maciej Walkowiak (Freelance Tech Lead)

Bez Hibernate’a też da się żyć!

Czas na rozdział, który jest alternatywną drogą, jaką możesz obrać. Ja oraz kilku moich kolegów w ostatnich latach doszliśmy do wniosku, że Hibernate w naszych projektach, bardziej szkodzi, niż pomaga. Zakasaliśmy rękawy i zdecydowaliśmy się przyjrzeć dostępnym alternatywom na rynku. Tych jest na rynku całkiem sporo, toteż na różnych etapach decydowaliśmy się na różne rozwiązania. Po czasie okazało się, że to lepsza droga, która bardziej odpowiada naszym potrzebom. Ma to oczywiście swoją drugą stronę medalu, ale póki co bilans zysków i strat wydaje się być pozytywny.

Owszem, nagłówek tego rozdziału brzmi trochę jak tekst kampanii poklepujący po plecach, że po amputacji istnieją dobre protezy. I tak trochę jest, bo niecały Hibernate jest zły, a alternatywy nie są pozbawione wad. Momentami ręka zawsze będzie ręką, a proteza nawet najlepsza jej nie zastąpi i będziemy musieli się trochę przyzwyczaić, że czasem oznacza to nieco więcej wysiłku z naszej strony. Chciałbym tylko zaznaczyć, że to nigdy nie była ręka pozbawiona wad i nie wycinalibyśmy jej bez powodu.

Całkowicie przypadkiem przygotowując tę suchą metaforę, przypomniałem sobie o pewnym filmie z 1999. https://www.imdb.com/title/tt0138510/mediaviewer/rm2481405440/

Skupię się więc na ofercie protez dostępnych na rynku. To druga strona medalu dla osób, które rozważają sprawdzenie alternatyw. Na sam koniec mam nadzieję, że wyniesiesz z tego wpisu, główne przesłanie, które abstrahuje od twojej decyzji i zachęci Cię do nauki SQL. Nie ważne bowiem, czy tak jak większość nadal będziesz używać najpopularniejszej drogi, czy spróbujesz innej drogi, SQL jako kompan twojej podróży przyda Ci się w obu przypadkach.

Pomimo, tego, że wiele raportów wskazuje na to, że Hibernate nadal może pochwalić się ponad 50% udziałem w rynku narzędzi do obsługi bazy z poziomu Javy, mocno rekomenduję Ci zrobić rozpoznanie samemu. Zamiast godzin nauki, jak współpracować z Hibernat’em, hektolitrów potu i łez wylanych z siebie, możesz dojść do wniosku, że cały ten framework, nie jest wart twojego czasu i lepiej zainwestować go w naukę SQL’a i alternatywnego rozwiązania.

W końcu będziesz mógł również zmienić stronę startową z blogu Vlada na np. Momentum. Czy u Ciebie ta droga się sprawdzi? Nie wiem, ale warto dać sobie szansę i spróbować. Czy rzeczywiście korzystasz z tych wszystkich dobrodziejstw Hibernate, czy bardziej walczysz z jego złożonością? A kiedy jesteś postawiony pod ścianą to i tak jedynym ratunkiem jest Native Query? Jeżeli tak, mocno polecam Ci skorzystanie z mojej rady.

JdbcTemplate czyli powrót do korzeni

Zaczynamy od samego spodu, czyli pisania czystego SQL przy wsparciu szablonów, jakie oferuje nam Spring. Największym problemem w tym podejściu jest brak walidacji na etapie pisania kodu (wspomniane kodowanie zapytania w Stringu) oraz późniejsza praca związana z koniecznością mapowania wyników do obiektów przy użyciu mapperów. A główna zaleta? Brak magii 🙂

Proste przykłady jak użyć JdbcTemplate znajdziesz tutaj https://www.baeldung.com/spring-jdbc-jdbctemplate

To nie jest alternatywa, która mocno rekomenduję, ale czasem, to w zupełności może wystarczyć. Sam spotkałem się z tego typu projektem. Zawsze z czasem, można tę część łatwo usprawnić, chociażby kolejnym na liście.

Będąc przy temacie czystego Jdbc warto wspomnieć o projekcie JDBI, który optymalizuje użycie z perspektywy użytkownika. Ponieważ, nie mam doświadczenia z tą biblioteką, nie poświęciłem jej więcej miejsca w tym wpisie. Więcej o projekecie możesz przeczytać w oficjalnej dokumentacji https://jdbi.org/

PS. O bibliotece dowiedziałem się przygotowując wpis https://kodujmy.pl/rewelacyjne-biblioteki-w-javie-ktorych-prawdopodobnie-nie-uzywasz-a-powinienes/ Jeżeli chciałbyś poznać więcej egzotycznych, a wartościowych narzędzi ze świata JVM powyższy wpis może przypaść Ci do gustu.

JOOQ czyli Jdbc na sterydach

JOOQ’a możemy rozumieć jako pójście o krok dalej i umożliwienie pisania czystego kodu SQL z poziomu klas Java. Jak sam autor przyznaje, rozwiązanie to stara się być tak blisko języka SQL jak to tylko możliwe. Wychodzi mu to całkiem nieźle. Zresztą spójrz na przykłady:

SQL:

SELECT AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME, COUNT(*)
FROM AUTHOR
JOIN BOOK ON AUTHOR.ID = BOOK.AUTHOR_ID
WHERE BOOK.LANGUAGE = 'DE'
AND BOOK.PUBLISHED > DATE '2008-01-01'
GROUP BY AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME
HAVING COUNT(*) > 5
ORDER BY AUTHOR.LAST_NAME ASC NULLS FIRST
LIMIT 2
OFFSET 1

To samo zapytanie napisane z poziomu klasy w JOOQ:

create.select(AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME, count())
      .from(AUTHOR)
      .join(BOOK).on(AUTHOR.ID.equal(BOOK.AUTHOR_ID))
      .where(BOOK.LANGUAGE.eq("DE"))
      .and(BOOK.PUBLISHED.gt(date("2008-01-01")))
      .groupBy(AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME)
      .having(count().gt(5))
      .orderBy(AUTHOR.LAST_NAME.asc().nullsFirst())
      .limit(2)
      .offset(1)

Można więc myśleć o tym rozwiązaniu jak o JDBC++. W przeciwieństwie do gołego JDBC kompilator w trakcie pilnuje czy zapytanie jest poprawne, co powoduje, że praca z tym frameworkiem jest niezwykle przyjemna i mniej podatna na błędy na późniejszym etapie runtime. Zakochałem się w nim od pierwszego wejrzenia. Praktyczne użycie JOOQ wygląda, bardzo często tak, że w pierwszej kolejności budujemy zapytanie SQL, a następnie przyklejamy je prosto do IDE do klasy z dostępem do bazy. Kilka drobnych zamian i mamy działające zapytanie z poziomu kodu.

Poniżej jeszcze garść właściwości JOOQ’a:

  • Silne typowanie na poziomie zapytań,
  • Brak nadmiarowego kodu przy chęci re użycia fragmentów zapytań,
  • Wynik zapytania to silnie typowany record, z toną metod pomocniczych,
  • Wynik zapytania może być również Streamem,
  • Dbanie o zamykanie zasobów

PS. Jeżeli nie planujesz wdrażać żadnej alternatywy i pozostać przy Hibernate to zainteresuj się przynajmniej projektem QueryDSL. Z JOOQiem współdzieli filozofię silnego typowania zapytań, dzięki czemu twoje życie stanie się o wiele prostsze. QueryDSL świetnie integruje się, z rozwiązaniem Spring DATA. Sprawdź ten artykuł: https://www.baeldung.com/rest-api-search-language-spring-data-querydsl

MyBatis czyli czysty SQL opakowany silnikiem szablonów

Jeszcze innym sposobem na dobranie się do danych są szablony zapytań SQL w MyBatis. Tutaj tak jak i przy poprzednikach SQL jest sercem tego rozwiązania, a cała reszta jak szablonowanie, instrukcje warunkowe, które można stosować w szablonach to tylko dodatek.

Przykład zapytania w ramach szablonu:

<select id="findActiveBlogWithTitleLike"
     resultType="Blog">
  SELECT * FROM BLOG
  WHERE state = ‘ACTIVE’
  <if test="title != null">
    AND title like #{title}
  </if>
</select>

Spring DATA JDBC czyli odchudzony Hibernate

Spring DATA JDBC, to stosunkowo młody projekt. Za cel obrał sobie okrojenie z większości złożonych elementów, jakie oferuje nam Hibernate i pozostawienie tylko prostych konstrukcji. Jak przyznają twórcy, rozwiązanie to od pierwszej linijki było tworzone w myśl zasady, aby wspierać Domain Driven Design. Różnica W stosunku do Spring DATA polega na tym, że nie znajdziesz tutaj między innymi:

  • sesji,
  • dirty checkingu,
  • nieintencjonalnych zapisów danych (musisz wywołać metodę save, aby dokonać insert lub update na bazie)
  • języka zapytań pokroju JPQL (zapytania piszesz tylko i wyłącznie w czystym SQL)
  • logiki odpowiedzialnej za generowanie schematu (i bardzo dobrze, pamiętasz punkt pierwszy, o którym wspominałem w radach dotyczących łatwiejszego życia z Hibernate? :-))

Wszystko to ma na celu pomóc w projektowaniu kodu przy zachowaniu zasad, o których wspominałem w rozdziale wyżej. To nieco radykalne podejście, ale skuteczne, ponieważ wiele osób, mając możliwość zrobienia rzeczy X, parafrazując zasady Murphego, zrobi to. A jeżeli nie umożliwimy im tego na starcie, zmusimy tym samym do lepszych praktyk.

Proste wprowadzenie do tego projektu na kanale Spring Academy.

Wszystkie drogi prowadzą do SQL

Na zakończenie mojego wpisu podzielę się wystąpieniem Lukasa Edera, które uwielbiam. Najbardziej przypadły mi do gustu fragmenty o jałowych dyskusjach i zobrazowanie w nieco przerysowany sposób różnicy między implementacją opartą na czystym SQL, a rozwiązaniem wykorzystującym Hibernate’a z poziomu kodu w Javie. Nie bez powodu mówi się ze najlepsi prelegenci to świetni komicy. W tym wystąpieniu humoru zdecydowanie nie brakuje.

Wszystkie wymienione przeze mnie alternatywy łączy jedno. W przeciwieństwie do Hibernate’a traktują one język SQL jako first class citizen. Ma to swoje konsekwencje w miejscach, gdzie styka się świat tabel i obiektów, gdzie dodatkowym narzutem może być tłumaczenie wyników i mapowanie ich na pola klas. Zaufaj mi jednak, że jest to o wiele prostszy problem, niż cały zakres tego czym jest w stanie zaskoczyć nas Hibernate.

Uważam, również, że sam SQL jest potężnym narzędziem, które przyda Ci się nie tylko do pisania warstwy dostępu do danych. Wiedzę, z tego zakresu z powodzeniem wykorzystasz później:
– w momencie poprawiania wydajności swoich aplikacji,
– do generowanie nawet najbardziej skomplikowanych raportów,
– procesowania dużej ilości danych,
– analityki danych, której całkiem dużo w dzisiejszych czasach, zaczynając od prostych selectów na twoim małym zbiorze danych, bo rozwiązania przygotowane typowo dla świata Big DATA,
– jako język, który zbuduje fundament, aby zrozumieć inne języki zapytań

Czujesz niedosyt?

Podzielę się jeszcze dwoma świetnymi artykułami w temacie:

A jakie jest twoje zdanie na temat Hibernate? Artykuły tego typu zawsze budzą gorące dyskusje. Jestem ciekawy, jakie jest twoje stanowisko w tej sprawie. Podziel się nim w komentarzu.