Active Record z Google Guice i JPA cześć II
W pierwszej części pokazałem jak powinna wyglądać standardowa encja oraz konfiguracja modułu. W tej części będzie trochę teorii co powinna dostarczać klasa SelfManagedEntity.
Active Record
Wzorzec projektowy Active Record ma swoje korzenie w kartotekowych bazach danych. Dokładnie w ich implementacjach z lat 70tych kiedy ktoś wymyślił, że pracując na rekordzie warto mieć metody typu save, delete, czy next.
W roku 2003 Martin Fowler opisał ten wzorzec i wskrzesił go po czasach niebytu na maszynach COBOLowo-LINCowych.
Jakie metody potrzebujemy
W wersji podstawowej potrzebujemy trzy metody.
Statyczny get
Metoda pozwala na wyszukanie encji w bazie danych na podstawie identyfikatora. W naszym przypadku w momencie gdy w bazie nie będzie takiego obiektu to użyjemy AI do jego stworzenia.
Niestatyczny delete
Usuwa encję z bazy.
Niestatyczny save
Zapisuje encję do bazy danych. W zależności od okoliczności wykonuje INSERT albo UPDATE.
Dlaczego nie ma oddzielnej metody update?
Ponieważ wywołując get nie wiemy czy encja pochodzi z DB czy jest nowym tworem. Dlatego wywołanie update mogło by powodować nieintuicyjne zachowanie aplikacji.
Jakie pola potrzebujemy
Jak wspomniałem we wpisie dotyczącym AI pola, które są wstrzykiwanie przez DI powinny być oznaczone jako transient jeżeli chcemy korzystać z JPA.
Niestatyczny EntityManager
W praktyce jedyne pole, które jest wymagane. Pozwala na dokonywanie operacji w bazie danych.
Statyczny EntityManager
Dodatkowy EntityManager, który będzie wykorzystywany w metodach statycznych. W zamian można użyć
Statyczny Injector
Z którego będziemy pobierać EntityManagera w metodach statycznych. Oba rozwiązania siebie warte.
EntityCache
Mój wynalazek. Jeżeli wywołujemy metodę get to nie wiemy czy obiekt o danym identyfikatorze istnieje w aplikacji. Jeżeli istnieje to mogą pojawić się problemy z równoległym utrwalaniem. Jeżeli aplikacja jest pisana w modelu jeden użytkownik na raz (JSE) to warto wprowadzić dodatkowy mechanizm, który będzie sprawdzał czy nie utworzono już obiektu o danym identyfikatorze. W przypadku aplikacji rozproszonej (WEB-UI) cache powinien ograniczać się do zakresu sesji danego użytkownika.
Tym jak zaimplementowany jest cache nie będę się zajmować. Nie jest obowiązkowy i w praktyce każdy może go napisać po swojemu.
Dodatkowe metody
Nasza zabawka nie będzie kompletna bez kilku dodatkowych metod.
Statyczny count
Zwraca ilość encji danego typu w bazie danych.
Statyczny max i min
Zwracający maksymalną bądź minimalną wartość z danej kolumny dla danej encji.
Statyczny findBy
Zwracający listę encji dla których dane kolumny spełniają dane warunki. Łączymy operatorem AND
I w praktyce co tylko możesz sobie wyobrazić.
Problem
Metody statyczne nie mogą być dziedziczone. Zatem albo będziemy powtarzać kod albo coś wykombinujemy. Rozwiązanie jest stosunkowo proste, ale będzie wymagać wprowadzenia dodatkowego kodu do naszych encji. Cóż… takie życie.