Kolejny wpis w stylu „nie róbcie tego w domu”. Ani w pracy, chyba iż nienawidzicie kolegów, to róbcie :D Pomysł przyszedł z (kolejnej) dyskusji na 4p na temat przydatności lomboka. W końcu lombok to antywzorzec, bo mamy rekordy. Mamy też JPA i tutaj rekordy to tak średnio działają.
Record a Entity
Założenie jest takie, iż nie da się używać javowych recordów jako encji. Po oznaczeniu rekordu dzięki @Entity dostaniemy błąd. I ma to sens. Encja powinna mieć bezparametrowy konstruktor oraz możliwość ustawienia pól. Rekord ma pola finalne i nie ma bezparametrowego konstruktora. Z dzisiejszego punktu widzenia, to jest bezsensowne, ale przypomnę nieśmiało, iż pierwsza specyfikacja JPA powstała w 2006r. To wtedy, gdy Spring dostał wsparcie dla adnotacji w ramach wersji 2.0 :) Od tego czasu trochę się pozmieniało.
Warto tu zaznaczyć, iż częściowe wsparcie dla rekordów będzie już w JPA 3.2, które wyjdzie z Jakarta EE 11… a ta miała wyjść czerwiec-lipiec 2024, ale coś nie pykło… bywa.
Częściowe wsparcie?
Ano częściowe, bo rekordów będzie można użyć jako klas osadzonych, czyli pól oznaczonych @Embedded. Ważniejsze dla nas jest jednak to, iż Hibernate już od pewnego czasu (ver. 6.2) wspiera to rozwiązanie. Otwiera to przed nami kilka ciekawych możliwości. Oczywiście jak wspomniałem wcześniej jest to zabawka, a nie rozwiązanie produkcyjne. Choć z drugiej strony… wszystko można wrzucić na produkcję, jak jest się wystarczająco odważnym.
Zabawmy się zatem w robienie encji…
Encja abstrakcyjna
Czyli klasyczny przykład woła roboczego każdej aplikacji. Zacznijmy od zdefiniowania klasy abstrakcyjnej, która będzie przechowywać podstawowe informacje o encji. Taka klasa zwykle zawiera też podstawowe konfiguracje, jak generatory dla identyfikatora, obsługę cache czy magię związaną ze śledzeniem wersji. Na razie wystarczy jednak konfiguracja dla id.
Listing 1. Abstrakcyjna encja
Na tym etapie jest ładnie miło i przyjemnie.
Czas na stworzenie encji.
Klasyczny User klasyczny
Chcąc stworzyć encję bazującą na tej z listingu 1, należałoby zrobić coś mniej więcej takiego:
Listing 2. Encja User zrobiona po bożemu
Lombok zabezpiecza nam gettery i settery.
Generyczna encja abstrakcyjna
Teraz jednak zrobimy „mały myk”, czyli nasza abstrakcyjna encja stanie się generyczna… i prawdopodobnie co poniektórzy już wiedzą, jak to się skończy :D
Listing 3. Abstrakcyjna encja po przejściach
Zaczyna się robić ciekawie. Taki potworek działa i ma się całkiem dobrze. Generyczność pojawiła się przed JPA, więc JPA wspiera ten mechanizm. Oczywiście, jak to z generycznością w Javie bywa, wszystko to trzyma się na trytytki, WD-40 i dobrą wolę kompilatora, ale działa.
Encja konkretna
Kolejnym krokiem będzie zmodyfikowanie naszej encji z listingu 2.
Listing 4. Encja po modyfikacji
I to też działa… i nasz rekord jest prawie encją i wszyscy są zadowoleni. No może poza tymi kilkoma malkontentami, którzy będą musieli to utrzymywać :D Oczywiście „rekordowość” musimy manualnie wyklepać. Metody, których nazwy nie posiadają przedrostków, czy konstruktor ustawiający pola biznesowe sam się nie napisze, choć akurat tu pomaga AI. Używa się tego jak normalnego rekordu, bo cały pomysł na rekordy jest taki, iż to „prekonfigurowane” klasy z finalnymi polami i kilkoma dodatkami.
Podsumowanie
To rozwiązanie to ciekawostka i tak należy ją traktować. Może być przydatne o ile mamy już model oparty o rekordy, a trzeba go jakoś utrwalać. Poza tym jest to na tyle niestandardowe rozwiązanie, iż utrzymanie go może być upierdliwe i kosztowne.