Relacje 1. Modelowanie relacji. - siedem rodzajów relacji J2EE. - relacje jedno i wielokierunkowe, - relacje reprezentowane przez kolekcje. 1
Relacje Modelowanie pojęć biznesowych wymaga łączenia komponentów encyjnych w relacje. Specyfikacja JEE określa siedem rodzajów relacji: jeden-do-jednego (one-to-one), jeden-do-wielu (one-to-many), wiele-do-jednego (many-to-one) oraz wiele-do-wielu (many-to-many). Relacja może mieć charakter jednokierunkowy (unidirectional) lub dwukierunkowy (bidirectional). 2
Jednokierunkowa relacja 1:1 CUSTOMER FIRST_NAME LAST_NAME ADDRES_ 1 1 ADDRESS STREET CITY ZIP @Entity public class Customer implements java.io.serializable { private Address address; @OneToOne(cascade={CascadeType.ALL) @JoinColumn(name="ADDRESS_") public Address getaddress( ) { return address; 3
Jednokierunkowa relacja 1:1 <entity-mappings> <entity class="com.titan.domain.customer" access="property"> <attributes> <id name="id"> <generated-value/> </id> <one-to-one name="address" targetentity="com.titan.domain.address" fetch="lazy" optional="true"> <cascade>all</cascade> <join-column name="address_"/> </one-to-one> </attributes> </entity> </entity-mappings> 4
Złączenie wg. klucza głównego @OneToOne @PrimaryKeyJoinColumn(cascade={CascadeType.ALL) public Address getaddress( ) { return address; Kontener automatycznie zmodyfikuje tabelę CUSTOMER: CREATE TABLE CUSTOMER( INT PRIMARY KEY NOT NULL, address_id INT, ) ALTER TABLE CUSTOMER ADD CONSTRAINT customerrefaddress FOREIGN KEY (address_id) REFERENCES ADDRESS (id); 5
Dwukierunkowa relacja 1:1 CUSTOMER FIRST_NAME LAST_NAME CREDIT_CARD_ 1 1 CREDIT_CARD NUMBER EXP_DATE Relacja dwukierunkowa polega na tym, ze jeden obiekt (encja) otrzymuje referencję do drugiego obiektu (encji) oraz drugi obiekt posiada referencję do pierwszego. Relacje dwukierunkowe są odwzorowane w relacyjnym modelu danych identycznie jak relacje jednokierunkowe. 6
Dwukierunkowa relacja 1:1 @Entity public class CreditCard implements java.io.serializable { private Customer customer; // nazwa atrybutu w klasie Customer, którego dotyczy mapowanie @OneToOne(mappedBy="creditCard") public Customer getcustomer( ) { return this.customer; Klasa Customer nie ulega zmianie w porównaniu z relacją jednokierunkową @Entity public class Customer implements java.io.serializable { private CreditCard creditcard; @OneToOne(cascade={CascadeType.ALL) @JoinColumn(name="CREDIT_CARD_") public CreditCard getcreditcard( ) { return creditcard; 7
Dwukierunkowa relacja 1:1 Przykład programu wiążącego encje Customer oraz CreditCard: Customer cust = new Customer( ); CreditCard card = new CreditCard( ); cust.setcreditcard(card); card.setcustomer(cust); entitymanager.persist(cust); Ponieważ w adnotacji OneToOne encji Customer ustawiliśmy atrybut cascade={cascadetype.all odpowiedni wpis w tabeli CREDIT_CARD zostanie utworzony automatycznie. 8
Strona właścicielska i przeciwna Ustawione w powyższy sposób odwzorowanie określa, że obiekt Customer jest stroną właścicielską (owning side) natomiast CreditCard stroną przeciwną (inverse side). Poprawna zmiana właściciela karty kredytowej musi odbywać się następująco: Customer newcust = em.find(customer.class, newcustid); CreditCard card = oldcustomer.getcreditcard( ); oldcustomer.setcreditcard(null); newcust.setcreditcard(card); natomiast jej usunięcie jest realizowane przez: Customer cust = em.find(customer.class, id); em.remove(cust.getcreditcard( )); cust.setcreditcard(null); 9
Dwukierunkowa relacja 1:1 <entity-mappings> <entity class="com.titan.domain.customer" access="property"> </entity> <entity class="com.titan.domain.creditcard" access="property"> <attributes> <id name="id"> <generated-value/> </id> <one-to-one name="customer" target-entity="com.titan.domain.customer" mapped-by="creditcard"/> </attributes> </entity> </entity-mappings> 10
Jednokierunkowa relacja 1:N CUSTOMER FIRST_NAME LAST_NAME 1 N PHONE NUMBER TYPE CUSTOMER_ @Entity public class Customer implements java.io.serializable { private Collection<Phone> phonenumbers = new ArrayList<Phone>( ); @OneToMany(cascade={CascadeType.ALL) @JoinColumn(name="CUSTOMER_") public Collection<Phone> getphones( ) { return phonenumbers; 11
Jednokierunkowa relacja 1:N Obsługa encji jest analogiczna jak w przypadku jednokierunkowej relacji jedendo-jednego. Customer cust = entitymanager.find(customer.class, pk); Phone phone = new Phone("123-456-7890"); // dodanie telefonu cust.getphones( ).add(phone); // usunięcie telefonu cust.getphones( ).remove(phone); entitymanager.remove(phone); 12
Jednokierunkowa relacja 1:N <entity-mappings> <entity class="com.titan.domain.customer" access="property"> <attributes> <id name="id"> <generated-value/> </id> <one-to-many name="phones" targetentity="com.titan.domain.phone"> <cascade-all/> <join-column name="customer_"/> </one-to-many> </attributes> </entity> </entity-mappings> 13
Relacja z wykorzystaniem tabeli złączenia CUSTOMER FIRST_NAME LAST_NAME CUSTOMER_PHONE 1 N N 1 CUSTOMER_ PHONE_ PHONE NUMBER TYPE @Entity public class Customer implements java.io.serializable { private Collection<Phone> phones; @OneToMany(cascade={CascadeType.ALL) @JoinTable(name="CUSTOMER_PHONE"), joincolumns={@joincolumn(name="customer_"), inversejoincolumns={@joincolumn(name="phone_")) public Collection<Phone> getphonenumbers( ) { return phones; 14
Relacja z wykorzystaniem tabeli złączenia <entity-mappings> <entity class="com.titan.domain.customer" access="property"> <attributes> <id name="id"> <generated-value/> </id> <one-to-many name="phones" targetentity="com.titan.domain.phone"> <cascade-all/> <join-table name="customer_phone"> <join-column name="customer_"/> <inverse-join-column name="phone_"/> </join-table> </one-to-many> </attributes> </entity> </entity-mappings> 15
Jednokierunkowa relacja N:1 CRUISE NAME SHIP_ N 1 SHIP NAME @Entity public class Cruise implements java.io.serializable { private Ship ship; @ManyToOne @JoinColumn(name="SHIP_") public Ship getship( ) { return ship; 16
Jednokierunkowa relacja N:1 <entity-mappings> <entity class="com.titan.domain.cruise" access="property"> <attributes> <id name="id"> <generated-value/> </id> <many-to-one name="ship" target-entity="com.titan.domain.ship" fetch="eager"> <join-column name="ship_"/> </many-to-one> </attributes> </entity> </entity-mappings> 17
Dwukierunkowa relacja 1:N CRUISE NAME SHIP_ 1 N RESERVATION AMOUNT_PA CRUISE_ Analogicznie jak w poprzednim wypadku relacja dwukierunkowa jest wykorzystywana gdy obie encje muszą wiedzieć o sobie nawzajem. W modelu programistycznym odpowiednie adnotacje są dodane do definicji obu klas. Zgodnie ze specyfikacją JPA stroną właścicielską jest komponent naprzeciwko encji z licznością równą jeden. W naszym przypadku to Reservation. 18
Dwukierunkowa relacja 1:N @Entity public class Reservation implements java.io.serializable { private Cruise cruise; @ManyToOne @JoinColumn(name="CRUISE_") public Cruise getcruise( ) { return cruise; @Entity public class Cruise implements java.io.serializable { private Collection<Reservation> reservations = new ArrayList<Reservation>( ); @OneToMany(mappedBy="cruise") public Collection<Reservation> getreservations( ) { return reservations; 19
Dwukierunkowa relacja 1:N <entity-mappings> <entity class="com.titan.domain.cruise" access="property"> <attributes> <id name="id"><generated-value/></id> <one-to-many name="ship" target-entity="com.titan.domain.reservation" fetch="lazy" mapped-by="cruise"> </one-to-many> </attributes> </entity> <entity class="com.titan.domain.reservation" access="property"> <attributes> <id name="id"><generated-value/></id> <many-to-one name="cruise" target-entity="com.titan.domain.cruise" fetch="eager"> <join-column name="cruise_"/> </many-to-one> </attributes> </entity> </entity-mappings> 20
Dwukierunkowa relacja N:N CUSTOMER FIRST_NAME LAST_NAME CUSTOMER_RESERVATION CUSTOMER_ RESERVATION_ RESERVATION AMOUNT_PA Relacja wiele-do-wielu w relacyjnym modelu danych odwzorowana z wykorzystaniem tabeli złączenia 21
Dwukierunkowa relacja N:N @Entity public class Reservation implements java.io.serializable { private Set<Customer> customers = new HashSet<Customer>( ); @ManyToMany @JoinTable(name="RESERVATION_CUSTOMER"), joincolumns={@joincolumn(name="reservation_"), inversejoincolumns={@joincolumn(name="customer_")) public Set<Customer> getcustomers( ) { return customers; @Entity public class Customer implements java.io.serializable { private Collection<Reservation> reservations = new ArrayList<Reservation>( ); @ManyToMany(mappedBy="customers") public Collection<Reservation> getreservations( ) { return reservations; 22
Dwukierunkowa relacja N:N <entity-mappings> <entity class="com.titan.domain.reservation" access="property"> <attributes><id name="id"><generated-value/></id> <many-to-many name="customers" target-entity="com.titan.domain.customer" fetch="lazy"> <join-table name="reservation_customer"> <join-column name="reservation_"/> <inverse-join-column name="customer_"/> </join-table> </many-to-many> </attributes> </entity> <entity class="com.titan.domain.customer" access="property"> <attributes><id name="id"><generated-value/></id> <many-to-many name="cruise" target-entity="com.titan.domain.reservation" fetch="lazy" mapped-by="customers"> </many-to-many> </attributes> </entity> </entity-mappings> 23
Jednokierunkowa relacja N:N CABIN NAME DECK CABIN_RESERVATION CABIN_ RESERVATION_ RESERVATION AMOUNT_PA @Entity public class Reservation implements java.io.serializable { @ManyToMany @JoinTable(name="CABIN_RESERVATION", joincolumns={@joincolumn(name="reservation_"), inversejoincolumns={@joincolumn(name="cabin_")) public Set<Cabin> getcabins( ) { return cabins; 24
Jednokierunkowa relacja N:N <entity-mappings> <entity class="com.titan.domain.reservation" access="property"> <attributes> <id name="id"> <generated-value/> </id> <many-to-many name="cabins" target-entity="com.titan.domain.cabin" fetch="lazy"> <join-table name="cabin_reservation"> <join-column name="reservation_"/> <inverse-join-column name="cabin_"/> </join-table> </many-to-many> </attributes> </entity> </entity-mappings> 25
Relacje reprezentowanie przez kolekcje JPA umożliwia stosowanie następujących kolekcji do reprezentowania relacji jedendo-wielu i wiele-do-wielu: java.util.collection, java.util.set, java.util.list i java.util.map. @ManyToMany @OrderBy("lastName ASC") @JoinTable(name="RESERVATION_CUSTOMER"), joincolumns={@joincolumn(name="reservation_"), inversejoincolumns={@joincolumn(name="customer_")) public List<Customer> getcustomers( ) { return customers; <many-to-many name="customers" target-entity="com.titan.domain.customer" fetch="lazy"> <order-by>lastname ASC</order-by> <join-table name="reservation_customer"> <join-column name="reservation_"/> <inverse-join-column name="customer_"/> </join-table> </many-to-many> 26
java.util.map @OneToMany(cascade={CascadeType.ALL) @JoinColumn(name="CUSTOMER_") @MapKey(name="number") public Map<String, Phone> getphonenumbers( ) { return phonenumbers; <one-to-many name="phonenumbers" target-entity="com.titan.domain.phone" fetch="lazy"> <cascade-all/> <map-key name="number"/> <join-column name="customer_"/> </one-to-many> 27
Podsumowanie JEE w ramach odwzorowania relacyjno-obiektowego umożliwia korzystanie z siedmiu typów relacji. Odwzorowanie jest określone poprzez mechanizm adnotacji lub konfigurację zapisaną w pliku persistance.xml. Aby modelować relacje obejmujące wiele obiektów możemy korzystać z czterech podstawowych typów kolekcji. 28