01 lutego 2007

Java Persistence - Rozdział 2 Entities - rozpoczęcie rozdziału

Rozdział rozpoczyna się definicją podstawowego bytu specyfikacji JPA - encją (ang. entity). Tak zwięźle zdefiniowany w specyfikacji, że aż trudno przetłumaczyć to na polski (ze zrozumieniem było znacznie łatwiej) - lekki (w sensie zależności ze światem zewnętrznym) obiekt trwały reprezentujący/modelujący pewien byt (co w angielskim brzmi a lightweight persistent domain object - lipido ;-)). Można przyjąć, że jest to pojęcie architektoniczne, trochę górnolotne.

Realizacją encji w rękach programisty jest klasa encji (również znana jako komponent encyjny, ang. entity class).

Klasa encji określona jest przez udekorowanie adnotacją @Entity lub poprzez deskryptor instalacji (bardzo przydatne, jeśli chcielibyśmy skorzystać z istniejących klas bez ich modyfikacji - chociaż nie wyobrażam sobie tak istotnych transformacji bez jakichkolwiek kolizji. Dodatkowo możliwe jest tworzenie komponentów encyjnych bez wprowadzania zależności od implementacji adnotacji JPA).

Klasa encji musi posiadać bezargumentowy konstruktor o widoczności public bądź protected. Inne konstruktory również możliwe.

Klasa encji musi być klasą główną (ang. top-level class) w klasyfikacji języka Java. Bardzo zastanowiło mnie stwierdzenie, że enum oraz interfejsy nie powinny być encjami - wydaje się, że jest to technicznie możliwe, skoro zawarto takie zdanie w specyfikacji, ale czemu miałoby służyć skorzystanie z tego?!

Klasa encji nie może być final, jak i metody oraz trwałe zmienne instancji (czyli te, które podlegają zarządzaniu przez kontener).

Jeśli instancja będzie przekazywana przez wartość jako odłączony obiekt (ang. detached object), np. jako wynik metody interfejsu zdalnego, klasa musi implementować interfejs java.io.Serializable. Generalnie, dobrą praktyką jest tworzenie klasy encji implementującej interfejs Serializable (nawet, jeśli nie dojdzie do przekazywania klasy zdalnie).

Komponenty encyjne wspierają dziedziczenie, polimorficzne powiązania i zapytania (a cóż to takiego?!)

Klasy abstrakcyjne oraz konkretne (nieabstrakcyjne) mogą być klasami encyjnymi.

Klasa encji może rozszerzać klasę nieencyjną i na odwrót.

Stan trwały encji jest wyznaczony przez zmienne instancji bądź właściwości (czyli metody operujące na zmiennych instancji zgodne z wytycznymi specyfikacji JavaBeans). Zmienne instancji mogą być wyłącznie "dotykane" przez metody encji. Zabronione jest bezpośrednie korzystanie ze zmiennych instancji przez klientów encji. Stan encji dostępny jest jedynie poprzez metody zarządzające (settery/gettery) i/lub metody biznesowe. Zmienne instancji mogą być private, protected, package.

Stan komponentu pozyskiwany jest przez środowisko JPA poprzez właściwości (metody JavaBeans - setters/getters) bądź zmienne instancji. Udekorowanie adnotacją wyznacza dostęp poprzez właściwość bądź zmienną instancji.
Jeśli korzystamy z dostępu poprzez zmienne, środowisko JPA dostaje się do zmiennych trwałych bezpośrednio. Wszystkie zmienne nie-transient, które nie są udekorowane przez @Transient są trwałe (podlegają zapisowi). W przypadku dekorowania zmiennych, tam również znajdują się adnotacje mapujące.
Jeśli korzystamy z dostępu poprzez właściwości, środowisko JPA dostaje się do zmiennych trwałych poprzez metody setX oraz getX. Wszystkie właściwości nieudekorowane przez @Transient są trwałe. Metody muszą być public lub protected. Jedynie metody odczytujące właściwości (gettery) mogą być udekorowane. Zabronione jest udekorowanie metody modyfikującej właściwości (setter). W przypadku dekorowania właściwości wyłącznie metody odczytujące są udekorowane adnotacjami mapującymi.

Adnotacje mapujące (o nich niebawem) nie mogą być przypisane do pól i właściwości transient czy @Transient.

Istnieje duże ryzyko nieokreśloności działania środowiska, jeśli udekorujemy jednocześnie pola i właściwości, lub jeśli zadeklarowano inne wartości mapowania w deskryptorze instalacji. Innymi słowy należy zagwarantować, aby dekorowanie przypadło albo polom albo właściwościom i aby deskryptor był w zgodzie z adnotacjami.

Wymagane jest, aby klasa encji była zgodna z wytycznymi specyfikacji JavaBeans odnośnie nazewnictwa metod (setX dla modyfikacji zmiennej X, getX/isX dla odczytania jej wartości).

Pola trwałe będące kolekcjami obiektów mogą być jedynie typu java.util.Collection, java.util.Set, java.util.List, java.util.Map. Uwaga na kolejność elementów w List, która nie jest gwarantowana, chyba że korzysta się z OrderBy. Możliwe jest korzystanie z generycznych postaci kolekcji, np. Set<Exam>.

Jeśli korzysta się z możliwości dodania logiki aplikacji do metod właściwości (np. kontrola w metodzie setter podczas modyfikacji) należy uważać na brak kolejności ich wywoływania na instancji.

Wyjątki niekontrolowane rzucone przez metody dostępu (właściwości) powodują wycofanie aktywnej tranzakcji. Podczas zapisu/odczytu, rzucone wyjątki powodują wycofanie aktywnej tranzakcji i rzucenie wyjątku PersistenceException opakowującego właściwy wyjątek aplikacyjny.

Podklasy mogą przysłaniać/nadpisywać metody właściwości. Nie zaleca się nadpisywania adnotacji mapowania z nimi związanymi.

Pola/właściwości trwałe mogą być następujących typów:
  • wszystkie typy proste Java
  • java.lang.String
  • dowolne, istniejące i stworzone przez programistę, typy serializowalne (implementujące interfejs Serializable, włączając typy opakowujące, java.math.BigInteger, java.math.BigDecimal, java.util.Date, java.util.Calendar, java.sql.Date, java.sql.Time, java.sql.Timestamp, byte[], Byte[], char[], Character[])
  • enumy
  • inne klasy encyjne lub ich kolekcje
  • klasy wbudowane (ang. embeddable classes) - o nich później
Metadane (adnotacje bądź deskryptor instalacji) mapowania precyzują domyślne mapowanie, ładowanie/zapis i relacje (więcej o tym w rozdziale 9).

Kolejna relacja (dokończenie rozdziału) jutro^H^H^H^H^Hdzisiaj.