02 lutego 2007

Java Persistence - Rozdział 2 Entities - kontynuacja lektury rozdziału

Poprzednią relację z rozdziału 2 - Entities - specyfikacji JPA zakończyłem na sekcji 2.1.1. Porcja kolejnych informacji wstępnych o encjach i ich mapowaniu.

Kolejna część rozdziału 2 rozpoczyna się prezentacją przykładowego komponentu encyjnego. Nie byłoby w nim nic nadzwyczajnego (POJO z annotacjami JPA), gdyby nie fakt, że klasa encji zawiera jedną metodę biznesową addPhone:

public void addPhone(PhoneNumber phone) {
this.getPhone().add(phone);
phone.addCustomer(this);
}

Zazwyczaj podobne metody posiadałem w innych klasach, nie będących encjami (np. sesyjnym komponentem) podczas, gdy encje były wyłącznie klasami z właściwościami (uważam za złą praktykę upublicznienie pól encji, czyli bezpośredni do nich dostęp). Faktycznie, możliwość umieszczenia metod w klasie encji, komponent nabiera rumieńców - staje się pełnoprawnym komponentem, a nie zwykłym DTO (ang. data transfer object - obiekt transferujący dane).

Tworzenie encji odbywa się identycznie jak instancji każdego innego typu w Javie - poprzez wywołanie dowolnego konstruktora za pomocą new. Instancja nie jest trwała doputy, dopóki nie wywołana zostanie metoda klasy EntityManager (innymi słowy, do momentu "zetknięcia" z EntityManager, klasa encji jest najzwyklejszą klasą w Javie i nie podlega jakieś magicznej obsłudze utrwalania, innej niż tej, której podlegają pozostałe byty w JVM).

Encja musi posiadać klucz unikatowy (ang. primary key). Klucz musi być zdefiniowany na encji, która jest początkiem hierarchii encji (pierwsza klasa udekorowana przez @Entity lub oznaczona jako encja przez deskryptor) lub na klasie nadrzędnej przywiązanej do hierarchii encji (ta część nie jest jeszcze dla mnie w pełni zrozumiała - oczekuję wyjaśnień w dalszej części specyfikacji).

Klucz unikatowy, prosty związany jest z pojedyńczą zmienną/właściwością instancji encji za pomocą adnotacji @Id.

Klucz unikatowy, złożony musi być związany z pojedyńczą zmienną/właściwością encji lub ich zbiorem. Konieczne jest zdefiniowanie dedykowanej klasy klucza i związanie z encją za pomocą adnotacji @EmbeddedId lub @IdClass (jak, co i kiedy w nadchodzących rozdziałach).

Klucze mogą składać się ze zmiennych/właściwości następujących typów:
  • dowolne typy proste i ich odpowiedniki opakowujące
  • java.lang.String
  • java.util.Date
  • java.sql.Date
Nie zalecane jest stosowanie kluczy opartych o typy zmienno-przecinkowe (float, double i ich odpowiedniki opakowujące).

Nie zalecane jest stosowanie typów w kluczach, innych niż wymienione, ze względu na możliwą nieprzenośność encji.

Zaleca się korzystanie z typów całkowito-liczbowych podczas korzystanie z generowanych kluczy unikatowych, ze względu na możliwą nieprzenośność encji.

Stosowanie java.util.Date jako klucza, wymaga stosowania typu DATE w mapowaniu (więcej informacji o mapowaniu w nadchodzących rozdziałach).

Dostęp do pól/właściwości klasy klucza unikatowego jest wyznaczony przez rodzaj dostępu encji z którą jest związany (sądziłem, że specyfikacja nie będzie już do tego wracała, więc teraz wyjaśniam). Rodzaj dostępu encji jest to sposób dotarcia do jej informacji (niezbędne do poprawnego działania środowiska JPA), albo bezpośrednio poprzez pole instancji (ang. field-based access) lub pośrednio, poprzez właściwości, czyli metody opakowujące - gettery i settery - (ang. property-based access).

Reguły tworzenia kluczy złożonych:
  • Klasa klucza musi być public z publicznym bezargumentowym konstruktorem (poza innymi)
  • Dostęp poprzez właściwość (do informacji encji) wymusza, aby właściwości (tj. metody opakowujące) były public lub protected
  • Klasa klucza musi być Serializable
  • Klasa klucza musi dostarczać metody equals oraz hashCode i muszą być zgodne z działaniem bazy, z którą jest związana
  • Klucz musi być klasą wbudowaną (o tym zaznaczono będzie później - ang. embeddable class) albo zmapowaną do wielu pól lub właściwości klasy
Jeśli klucz jest zmapowany do wielu pól i właściwości encji, nazwy i ich typy (między klasą klucza a klasą encji) muszą być takie same.

I ostatnia ważna informacja z sekcji o kluczach głównych (a wydawałoby się oczywista) opartych o typy modyfikowalne (ang. mutable types) to fakt, że wartość klucza głównego nie może się zmienić (czyli zmiana wartości pola/właściwości będącym kluczem lub jego składową). Niektóre implementacje JPA mogą rzucić wyjątek w takich sytuacjach, ale nie powinno się na to liczyć i encja może być nieprzenośna.

Klasy wbudowalne (ang. embeddable classes) - encja może składać się z klas reprezentujących jej stan. Instancje tych klas, w przeciwieństwie do instancji encji, nie posiadają identyfikatora trwałości. Powinny być udekorowane adnotacją @Embeddable lub zdefiniowane w deskryptorze oraz spełniać wymagania stawiane klasom encji.

Jeśli pole lub właściwość trwała, inna niż właściwość relacji, nie są udekorowane jedną z adnotacji mapujących (lub ich mapowania nie są opisane przez deskryptor) zachodzą następujące reguły:
  • Jeśli ich typem jest klasa udekorowana @Embeddable, są one mapowane jak, gdyby były one udekorowane @Embedded (więcej w kolejnych rozdziałach).
  • Jeśli ich typem jest jeden z następujących typów:
    • typy prymitywne lub ich typy opakowujące
    • java.lang.String
    • 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
    • dowolny typ implementujący Serializable
są one mapowane jak, gdyby były one udekorowane adnotacją @Basic.

Jeśli żadna reguła nie zachodzi, pojawi się błąd.

Na dzisiaj wystarczy tej wiedzy.