Dziedziczenie i język EJB QL
Transkrypt
Dziedziczenie i język EJB QL
Dziedziczenie i język EJB QL 1. Odwzorowanie hierarchii komponentów encyjnych ● SINGLE_TABLE, ● TABLE_PER_CLASS, ● JOINED. 2. Podstawy języka zapytań EJB QL ● interfejs Query, ● proste zapytania, ● relacje i złączenia, ● operatory IS EMPTY, MEMBER OF, ● podzapytania zapytania nazwane. 1 Komponenty encyjne a dziedziczenie Specyfikacja Java Persistence Api pozwala opisać dziedziczenie, polimorfizm, relacje oraz zapytania polimorficzne. JPA dopuszcza trzy sposoby odwzorowania hierarchii encji: - pojedyncza tabela dla całej hierarchii klas, - osobna tabela dla każdej klasy, - osobna tabela dla każdej podklasy. 2 Komponenty encyjne a dziedziczenie public class private private private Person implements java.io.Serializable{ int id; String firstName; String lastName; ... } public class private private private private ... } Customer extends Person { String street; String city; String state; String zip; public class Employee extends Customer { private int employeeId; ... } 3 Pojedyncza tabela Pojedyncza tabela odwzorowuje wszystkie klasy w ramach danej hierarchii. create table PERSON_HIERARCHY( id integer primary key not null, firstName varchar(255), lastName varchar(255), street varchar(255), city varchar(255), state varchar(255), zip varchar(255), employeeId integer, DISCRIMINATOR varchar(31) not null ); 4 Pojedyncza tabela @Entity @Table(name="PERSON_HIERARCHY") @Inheritance(strategy=InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name="DISCRIMINATOR", discriminatorType=DiscriminatorType.STRING) @DiscriminatorValue("PERSON") public class Person implements java.io.Serializable{ ... } @Entity @DiscriminatorValue("CUST") public class Customer extends Person { ... } @Entity // domyślny dyskryminator: Employee – nazwa komponentu public class Employee extends Customer { ... } 5 Pojedyncza tabela <entity-mappings> <entity class="com.titan.domain.Person"> <inheritance strategy="SINGLE_TABLE"/> <discriminator-column name="DISCRIMINATOR" discriminator-type="STRING"/> <discriminator-value>PERSON</discriminator-value> <attributes> <id> <generated-value/> </id> </attributes> </entity> <entity class="com.titan.domain.Customer"> <discriminator-value>CUST</discriminator-value> </entity> <entity class="com.titan.domain.Employee"/> </entity-mappings> 6 Pojedyncza tabela Zalety: - prosta koncepcyjnie, - duża wydajność. Wady: - dużo pól o wartości NULL, - atrybuty klas potomnych muszą przyjmować wartości NULL, - brak postaci normalnej. 7 Osobne tabele dla klas create table Person ( id integer primary key not null, firstName varchar(255), lastName varchar(255), ); create table Customer ( id integer primary key not null, firstName varchar(255), lastName varchar(255), create table Employee ( street varchar(255), id integer primary key not null, city varchar(255), firstName varchar(255), state varchar(255), lastName varchar(255), zip varchar(255), street varchar(255), ); city varchar(255), state varchar(255), zip varchar(255), employeeId integer, ); 8 Osobne tabele dla klas @Entity @Inheritance(strategy=InheritanceType.TABLE_PER_CLASS) public class Person implements java.io.Serializable{ ... } @Entity public class Customer extends Person { ... } @Entity public class Employee extends Customer { ... } 9 Osobne tabele dla klas <entity-mappings> <entity class="com.titan.domain.Person"> <inheritance strategy="TABLE_PER_CLASS"/> <attributes> <id> <generated-value/> </id> </attributes> </entity> <entity class="com.titan.domain.Customer"/> <entity class="com.titan.domain.Employee"/> </entity-mappings> 10 Osobne tabele dla klas Zalety: - możliwość określenia dodatkowych warunków dla atrybutów (np. wartość domyślna), - prostsze odwzorowanie. Wady: - brak postaci normalnej, - redundancja, - niska wydajność: dużo dodatkowych operacji w celu odwzorowania relacji itp. Zwykle nie zaleca się stosowania tej strategii odwzorowania hierarchii klas. 11 Osobne tabele dla podklas create table Person ( id integer primary key not null, firstName varchar(255), lastName varchar(255), ); create table Customer ( id integer primary key not null, street varchar(255), city varchar(255), state varchar(255), zip varchar(255), ); create table Employee ( EMP_PK integer primary key not null, employeeId integer ); 12 Osobne tabele dla podklas @Entity @Inheritance(strategy=InheritanceType.JOINED) public class Person { ... } @Entity public class Customer extends Person { ... } @Entity @PrimaryKeyJoinColumn(name="EMP_PK") public class Employee extends Customer { ... } 13 Osobne tabele dla podklas <entity-mappings> <entity class="com.titan.domain.Person"> <inheritance strategy="JOINED"/> <attributes> <id> <generated-value/> </id> </attributes> </entity> <entity class="com.titan.domain.Customer"/> <entity class="com.titan.domain.Employee"> <primary-key-join-column name="EMP_PK"/> </entity> </entity-mappings> 14 Osobne tabele dla podklas Zalety: - możliwość określenia dodatkowych warunków dla atrybutów (np. wartość domyślna), - prostsze odwzorowanie, - zwykle większa wydajność niż TABLE_FOR_CLASS, - model normalizowalny. Wady: - mniejsza wydajność niż SINGLE_TABLE. 15 Nieencyjne klasy bazowe @MappedSuperclass public class Person { @Id @GeneratedValue public int getId( ) { return id; } ... } @Entity @Table(name="CUSTOMER") @Inheritance(strategy=InheritanceType.JOINED) // atrybut lastName z nadklasy będzie odwzorowany w tabeli CUSTOMER // w pole SURNAME (domyślnie byłby w polu lastName) @AttributeOverride(name="lastName", column=@Column(name="SURNAME")) public class Customer extends Person { ... } @Entity @Table(name="EMPLOYEE") @PrimaryKeyJoinColumn(name="EMP_PK") public class Employee extends Customer { ... } 16 Nieencyjne klasy bazowe <entity-mappings> <mapped-superclass class="com.titan.domain.Person"> <attributes> <id> <generated-value/> </id> </attributes> </mapped-superclass> <entity class="com.titan.domain.Customer"> <inheritance strategy="JOINED"/> <attribute-override name="lastName"> <column name="SURNAME"/> </attribute-override> </entity> <entity class="com.titan.domain.Employee"> <primary-key-join-column name="EMP_PK"/> </entity> </entity-mappings> 17 Elementy EJB QL EJB QL jest deklaratywnym językiem zapytań służącym do przetwarzania obiektów Javy. Zapytania zapisane w języku EJB QL są tłumaczone przez kontener komponentów na jedno lub kilka zapytań SQL, które są kierowane do bazy danych poprzez interfejs JDBC. Język EJB QL nie zawsze jest wystarczającym narzędziem jednak jego stosowanie gwarantuje przenośność 18 Elementy EJB QL try { Query query = entityManager.creatQuery( "from Customer c where c.firstName='Bill' and c.lastName='Burke'"); Customer cust = (Customer)query.getSingleResult( ); } catch (EntityNotFoundException notFound) { } catch (NonUniqueResultException nonUnique){ } Wykonywanie zapytań odbywa się za pomocą interfejsu Query, który nieco przypomina, stosowany w JDBC interfejs java.sql.PreparedStatement. Odbieranie wyników zapytania jest możliwe poprzez dwie metody: public List getResultList( ); public Object getSingleResult( ); 19 Elementy EJB QL Parametry pełnią identyczną rolę jak w przypadku PreparedStatement mogą być nazywane (zalecane): Query query = entityManager.createQuery( "from Customer c where c.firstName=:first and c.lastName=:last"); query.setParameter("first", first); query.setParameter("last", last); java.util.List list = query.getResultList( ); oraz numerowane: Query query = entityManager.createQuery( "from Customer c where c.firstName=?1 and c.lastName=?2"); query.setParameter(1, first); query.setParameter(2, last); query.getResultList( ); 20 Proste zapytania SELECT OBJECT( c ) FROM Customer AS c SELECT c FROM Customer AS c SELECT c FROM Customer c Jeśli klauzula SELECT obejmuje więcej niż jedną kolumnę pojedynczym wynikiem jest tablica Object[]. Query query = manager.createQuery( "SELECT c.firstName, c.lastName FROM Customer AS c"); List results = query.getResultList( ); Iterator it = results.iterator( ); while (it.hasNext( )) { Object[] result = (Object[])it.next( ); String first = (String)result[0]; String last = (String)result[1]; } 21 Proste zapytania public class Name { private String first; private String last; public Name(String first, String last) { this.first = first; this.last = last; } ... } SELECT new Name(c.firstName, c.lastName) FROM Customer c W wyniku zapytania zostanie zwrócony obiekt Name lub ich lista. 22 Zapytania a relacje Customer 1:1 CreditCard N:1 CreditCompany 1:1 Address SELECT c.creditCard.creditCompany.address.city FROM Customer AS c @Entity public class Address { ... private ZipCode zip; } public class ZipCode implements java.io.Serializable { public int mainCode, codeSuffix; ... } SELECT c.address.zip.mainCode FROM Customer AS c // źle @Entity public class Address { ... @Embedded private ZipCode zip; } SELECT c.address.zip.mainCode FROM Customer AS c // dobrze 23 Zapytania a relacje (JOIN) Customer N:N Reservation N:1 Cruise N:1 Ship N:1 SELECT c.reservations.cruise FROM Customer AS c // źle EJB QL customer.getReservations().getCruise(); // źle Java SELECT r.cruise FROM Customer AS c, IN( c.reservations ) r SELECT r.cruise FROM Customer c INNER JOIN c.reservations r SELECT r.cruise FROM Customer c JOIN c.reservations r SELECT cb.ship FROM Customer c JOIN c.reservations r JOIN r.cabins cb 24 Zapytania a relacje (LEFT JOIN) Customer 1:N Phone SELECT c.firstName, c.lastName, p.number FROM Customer c LEFT JOIN c.phones p Adam Kot (12)654 32 11 Julia Zielińska (17)908 23 44 Julia Zielińska 0 690 579 870 Aneta Czarnecka null SELECT c.firstName, c.lastName, p.number FROM Customer c LEFT OUTER JOIN c.phones p SELECT c.firstName, c.lastName, p.number FROM Customer c LEFT JOIN FETCH c.phones p 25 Klauzula WHERE, operator IS EMPTY Podobnie jak w języku SQL, klauzula WHERE obsługuje porównania, operacje arytmetyczne, logiczne oraz operatory IN, BETWEEN, LIKE, IS NULL. Inne operatory: IS EMPTY // rejsy bez rezerwacji: SELECT crs FROM Cruise AS crs WHERE crs.reservations IS EMPTY // rejsy z co najmniej jedną rezerwacją: SELECT crs FROM Cruise AS crs WHERE crs.reservations IS NOT EMPTY // niepoprawne – zbiór wynikowy: rozpatrywane są tylko rezerwacje z niepustymi customers ze względu na wcześniejsze złączenie. SELECT r FROM Reservation AS r INNER JOIN r.customers AS c WHERE r.customers IS NOT EMPTY AND c.address.city = 'Boston' 26 Klauzula WHERE, operator MEMBER OF // lista rejsów dla których dany klient dokonał rezerwacji: SELECT crs FROM Cruise AS crs, IN (crs.reservations) AS res, Customer AS cust WHERE cust = :myCustomer AND cust MEMBER OF res.customers // lista rejsów dla których dany klient nie dokonał rezerwacji: SELECT crs FROM Cruise AS crs, IN (crs.reservations) AS res, Customer AS cust WHERE cust = :myCustomer AND cust NOT MEMBER OF res.customers Jeśli sprawdzamy, czy encja jest składową kolekcji pustej otrzymamy wartość false. EJB QL umożliwia stosowanie podstawowych funkcji i operacji agregujących. Można także używać klauzul DISTINCT, ORDER BY, GROUP BY oraz HAVING. 27 Podzapytania // liczba rezerwacji o wartości większej niż wartość średnia SELECT COUNT(res) FROM Reservation res WHERE res.amountPaid > (SELECT avg(r.amountPaid) FROM Reservation r) // rejsy z zapłaconymi wszystkimi rezerwacjami SELECT cr FROM Cruise cr WHERE 0 < ALL (SELECT res.amountPaid FROM cr.reservations res) // rejsy z co najmniej jedną nie zapłaconą rezerwacją ANY <-> SOME SELECT cr FROM Cruise cr WHERE 0 = ANY (SELECT res.amountPaid from cr.reservations res); 28 Zapytania nazwane @NamedQueries({ @NamedQuery(name="findFullyPaidCruises", query= "FROM Cruise cr WHERE 0 < ALL ( SELECT res.amountPaid from cr.reservations res)") }) @Entity public class Cruise {...} ... // wywołanie zapytania Query query = em.createNamedQuery("findFullyPaidCruises"); <entity-mappings> <named-query name="findFullyPaidCruises"> <query> FROM Cruise cr WHERE 0 < ALL ( SELECT res.amountPaid from cr.reservations res) </query> </named-query> </entity-mappings> 29 Inne operacje Interfejs Query umożliwia także wykonywanie zbiorowej aktualizacji i usuwania rekordów (UPDATE, DELETE) za pośrednictwem metody executeUpdate(). Istnieje także możliwość bezpośredniego wywołania zapytań SQL oraz zapytań nazwanych SQL. 30 Podsumowanie Specyfikacja JPA umożliwia elastyczne odwzorowanie obiektów Javy do relacyjnego modelu danych. Do podstawowych cech takiego odwzorowania dodano możliwość dziedziczenia komponentów encyjnych oraz określono funkcjonalny, zapewniający przenośność obiektowy język zapytań. 31