czwartek, 27 marca 2008

JPA - obiekty wbudowane (komponenty)

Wstęp

Obiekty wbudowane stosuje się w przypadku rozbieżności modelu obiektowego i fizycznego modelu bazodanowego. Przykładowo z bytem Użytkownik jest związany byt Adres. W modelu obiektowym byty te będą implementowane przez odrębne obiekty, jednak w relacyjnej bazie danych w celu osiągnięcia lepszej wydajności mogą być zaimplementowane przy użyciu jednej tabelki, zawierającej dane zarówno Użytkownika jak i jego Adres. W tym przykładzie Adres będzie obiektem wbudowanym i będzie przynależeć do trwałego obiektu Użytkownik. Ważną cechą obiektów wbudowanych jest to, że nie mają swojej tożsamości trwałej (swojego identyfikatora) i nie mogą być współdzielone przez różne obiekty trwałe.

Klasy wbudowywane

Klasa obiektu wbudowanego musi być oznaczona adnotacją Embeddable. Nie oznacza się jej jako encji, tym bardziej nie określa się dla niej tabeli w bazie danych ani identyfikatora. Poza tym dla klasy wbudowywanej specyfikuje się mapowanie pól jak przypadku zwykłych encji. Poniżej znajduje się przykład definicji klasy wbudowywanej - Adres:
package pl.dwalczak.jpapg2.model;
import javax.persistence.Column;
import javax.persistence.Embeddable;

@Embeddable
public class Address {
 private String city;
 private String postcode;
 private String street;
 private String number;

 @Column(length=50, nullable=false)
 public String getCity() {
  return city;
 }
 public void setCity(String city) {
  this.city = city;
 }

 @Column(length=6, nullable=false)
 public String getPostcode() {
  return postcode;
 }
 public void setPostcode(String postcode) {
  this.postcode = postcode;
 }

 @Column(length=50, nullable=false)
 public String getStreet() {
  return street;
 }
 public void setStreet(String street) {
  this.street = street;
 }

 @Column(length=10, nullable=false)
 public String getNumber() {
  return number;
 }
 public void setNumber(String number) {
  this.number = number;
 }
}

Umieszczanie obiektów wbudowywanych w obiektach trwałych

Pola klasy o typie oznaczonym jako wbudowywany opatruje się adnotacją Embedded. Nie jest to jednak niezbędne, gdyż specyfikacja JPA nakłada obowiązek na EntityManager aby sam wybadał sprawę na podstawie adnotacji Embeddable, którym jest opatrzona klasa danego pola. Pola obiektu wbudowanego są mapowane na kolumny tabeli encji, do której przynależy.
Nadpisywanie atrybutów mapowania
JPA umożliwia nadpisywanie atrybutów mapowania zdefiniowanych w klasie wbudowywanej. W tym celu używa się adnotacji AttributeOverrides, która zawiera listę adnotacji AttributeOverride. I te dopiero służą do nadpisywania mapowania poszczególnych pól klasy. Mechanizm nadpisywania atrybutów mapowania daje duże możliwości w kontekście wielokrotnego wykorzystania definicji klas wbudowywancyh. Poniżej znajduje się przykład encji, w skład której wchodzą dwa obiekty wbudowane o typie Adres (stały adres zamieszkania i adres korespondencyjny).
package pl.dwalczak.jpapg2.model;
import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides;
import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQuery;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;

@NamedQuery(
 name="user.findByNickName",
 query="select u from User u where u.nickName = :nickName"
)

// Definicja sekwencji "users_seq" służącej do generowania klucza głównego tabeli "users".
@SequenceGenerator(name="users_seq")

// Mapowanie tabeli "users".
@Entity
@Table(name="users")
public class User {
 private Long id;
 private String nickName;
 private Address address;
 private Address mailingAddress;

 @Id
 @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="users_seq")
 @Column(name="usr_id")
 public Long getId() {
  return id;
 }
 protected void setId(Long id) {
  this.id = id;
 }

 @Column(name="usr_nickname", unique=true, nullable=false, length=24)
 public String getNickName() {
  return nickName;
 }
 public void setNickName(String name) {
  this.nickName = name;
 }
 
 @Embedded
 public Address getAddress() {
  return address;
 }
 public void setAddress(Address address) {
  this.address = address;
 }
 
 @Embedded
 @AttributeOverrides({
  @AttributeOverride(name="city", column=@Column(name="ma_city")),
  @AttributeOverride(name="postcode", column=@Column(name="ma_postcode")),
  @AttributeOverride(name="street", column=@Column(name="ma_street")),
  @AttributeOverride(name="number", column=@Column(name="ma_number"))
 })
 public Address getMailingAddress() {
  return mailingAddress;
 }
 public void setMailingAddress(Address mailingAddress) {
  this.mailingAddress = mailingAddress;
 }
}

Zasoby

Pliki źródłowe przykładu
Hibernate Annotations - 2.2.2.3. Embedded objects (aka components)
Specyfikacja JPA

Brak komentarzy: