Agenda PostgreSQL Standard J2EE: JPA co to jest przegląd możliwości mapowanie hierarchii metody definiowania kwerend Natywny SQL zapytania nazwane JPQL Criteria API Hibernate Spring Data Przykład Grzegorz Wilaszek, Wojciech Krzystek
PostgreSQL "The world's most advanced open source database"
Historia początkowo opracowywany na Uniwersytecie Kalifornijskim w Berkely (Ingres) pierwsze wydanie (Postgres95): 1 maja 1995 obecnie pracuje nad nim organizacja opensource'owa: PostgreSQL Global Development Group wspierany przez komercyjnych dostawców rozwiązań bazodanowych najnowsza wersja: 9.2.4 (4 kwietnia 2013)
Czemu słoń? Wersja zachodnia i wersja wschodnia: Podobno szukano zwierzącego logo. Ktoś zapropownował słonia. Bo słoń zapamiętuje. Powiązane technologie: slonik, slon, slony replication (rus. слоны)
Wsparcie systemów operacyjnych i architekrut Systemy operacyjne: Linux (wszystkie niedawne dystrybucje), Windows(Win2000 SP4 i późniejszy), FreeBSD, OpenBSD, NetBSD, Mac OS X, AIX, BSD/OS, HP-UX, IRIX, OpenIndiana, OpenSolaris, SCO OpenServer, SCO UnixWare, Solaris, Tru64 Unix Architekruty: x86, x86-64, IA64 Itanium, PowerPC, PowerPC 64, S/390, S/390x, SPARC, SPARC 64, Alpha, ARM, MIPS, MIPSel, M68k, PA-RISC, M32R, NS32k i VAX
Funkcje składowe w bazie danych PL/pgSQL (podobny do proceduralnego języka PL/SQL w bazie Oracle) PL/Python PL/Perl PL/Tcl język SQL Dostępne do zainstalowania rozszerzenia: języki skryptowe: (np. plphp, PL/Ruby, PL/sh) języki kompilowane: C, C++ oraz Java (jako PL/Java); język statystyczny R jako PL/R.
Indeksy Indeksy funkcyjne - funkcja a nie wartość kolumny Indeksy częściowe - dla części tabeli przez dodanie WHERE na końcu CREATE INDEX Możliwość przeglądania indeksów od końca "index-only" skany - korzystanie tylko z indeksu podczas przeglądania bazy Przykładowe typy indeksów: B-drzewo, Hash, R-drzewo i GiST
Wyzwalacze Reguły: np. "INSTEAD OF" pozwala wstawić (INSERT) dane do widoku zamiast do tabeli Możliwość definiowania wyzwalaczy na widokach
Typy danych... standardowe... typy do wyszukiwania pełnotekstowego (tsvector, tsquery) typy geometryczne (point, line, lseg, box, path, polygon, circle) typy adresów sieciowych (cidr, inet, macaddr) XML, obsługujący również wyrażenia XPath (od wersji 8.3) UUID (od wersji 8.3) JSON (od wersji 9.2) typy zakresowe (Range Type) (od wersji 9.2)
Ograniczenia wielkości elementów Maksymalny rozmiar bazy: bez ograniczeń Maksymalny rozmiar tabeli: 32 TB Maksymalny rozmiar wiersza: 1,6 TB Maksymalny rozmiar pola w wierszu: 1 GB Maksymalna liczba wierszy w tabeli: bez ograniczeń Maksymalna liczba kolumn w tabeli: 250-1600 (zależy od rodzaju kolumn) Maksymalna liczba indeksów dla tabeli: bez ograniczeń
ORM w Javie
JPA (Java Persistence API) pakiety javax.persistence.* ORM (Object-Relational Mapping) tłumaczenie Kompozycji (kolekcje, a nawet mapy!) tłumaczenie dziedziczenia zwykłe javowe objekty (POJO) nie czujemy że pracujemy z bazą danych szeroka gama persistence providerów Hibernate, EclipseLink, ObjectDB, DataNucleus, OpenJPA,... 2 możliwości konfiguracji: adnotacje (zaprezentowane tutaj) pliki konfiguracyjne xml
JPA - Architektura
Wymagania w stosunku do POJO adnotacja javax.persistence.entity Musi mieć bezargumentowy konstruktor protected, lub public (Może mieć inne konstruktory) Klasa nie może być final, nie może mieć metod final (wg. standardu - jenak np. Hibernate sobie z tym radzi) warto zaimplementować Serializable - przesyłanie encji przez wartość do innego EntityMaganera mogą dziedziczyć po nie-encjach. Mogą być klasach bazowymi nie-encji Persystowane pola encji nie mogą być public. Dostęp jedynie przez gettery i settery
Strategie łączenia się z bazą validate - jeśli na wejściu nie mamy dobrej schemy to kończymy update - dorabiamy to czego brakuje create - tworzymy brakujące tabele create-drop - jest inaczej? zrzucamy tabele
Przykładowe persystowane klasy - encje @Entity public class Person { @Id @GeneratedValue(strategy=GenerationType.AUTO) private Integer id; @Entity public class Address { @Id @GeneratedValue(strategy=GenerationType.AUTO) private Integer id; private String firstname; private String street; private String lastname; private String city; @OneToMany private Set<Address> addresses; protected Address() { protected Person() { public Person(String firstname, String lastname) { this.firstname = firstname; this.lastname = lastname; //... setters, getters public Address(String street, String city) { this.street = street; this.city = city; //... setters, getters
Co możemy persystować w klasie? prymitywy typy otoczkowe enumy String, BigInteger. BigDecimal, Date, Calendar,... typy serializowalne (również user-defined) inne encje typy Embeddable kolekcje powyższych tablice powyższych @Entity public class Person { @Id private long id... private String name; private String surname; @Embedded private Address address; @Embeddable public class Address { private String street;... private String city;
Entity a dziedziczenie Trzy strategie: SINGLE_TABLE (default) JOINED TABLE_PER_CLASS
Mapowanie dziedziczenia: SINGLE_TABLE @Entity @Inheritance @DiscriminatorColumn(name="DTYPE") public abstract class Project { @Id private long id;... @Entity @DiscriminatorValue( discriminatortype=string, "L") public class LargeProject extends Project { private BigDecimal budget; @Entity @DiscriminatorValue( discriminatortype=string, "S") public class SmallProject extends Project { DTYPE id name budget L 1 proja 10 000 S 2 projb <null> tabela project Mało tabeli Nieużywane komórki Dobra wydajność gdy dużo klas w hierarchii Ryzyko dużego rozrostu 1 tabeli
Mapowanie dziedziczenia: JOINED @Entity @Inheritance(strategy=InheritanceType. JOINED) @DiscriminatorColumn(name="DTYPE") public abstract class Project { @Id private long id;... @Entity @DiscriminatorValue( discriminatortype=string, "L") public class LargeProject extends Project { private BigDecimal budget; @Entity @DiscriminatorValue( discriminatortype=string, "S") public class SmallProject extends Project { DTYPE id name L 1 proja S 2 projb tabela project id budget 1 10 000 tabela large_project id 2 tabela small_project
Mapowanie dziedziczenia: TABLE_PER_CLASS @Entity @Inheritance(strategy= InheritanceType.TABLE_PER_CLASS) public abstract class Project { @Id private long id;... @Entity public class LargeProject extends Project { private BigDecimal budget; @Entity public class SmallProject extends Project { tabela project - BRAK id name budget 1 proja 10 000 tabela large_project id name 2 projb tabela small_project
Metody definiowania kwerend
Natywny (zależny od DBMS!) SQL String sqlquery = "select night.id nid, night.night_duration, night.night_date, area.id aid, night.area_id, area.name " + "from Night night, Area area where night.area_id = area.id " + "and night.night_duration >=?"; Query q = getentitymanager().createnativequery(sqlquery, "GetNightAndArea"); q.setparameter( 1, expectedduration ); q.getresultlist();
JPQL (Java Persistence Query Language) EntityManager em = getentitymanager(); Query query = em.createquery( "select e from Employee e where e.address.city = :city"); query.setparameter("city", "Ottawa"); List<Employee> employees = query.getresultlist();
JPQL vs. SQL operujemy na obiektach: user.address.city.major brak niektórych słów kluczowch, np: LIMIT szybkość działania (JPQL pod maską ma SQL) JOIN nie jest JOINem: SELECT DISTINCT mag FROM Magazine mag JOIN mag.articles art JOIN art.author auth WHERE auth.firstname = 'John' Encje muszą być połączone: Magazine List<Article> Author Do skomplikowanych zapytań pozostają nam podzapytania
Zapytania nazwane (predefiniowane) Definicja: @NamedQuery( name="findallemployeesincity", query="select e from Employee e where e.address.city = :city") public class Employee {... Użycie: EntityManager em = getentitymanager(); Query q = em.createnamedquery("findallemployeesincity"); q.setparameter("city", "Ottawa"); List<Employee> employees = q.getresultlist();
Criteria API vs. JPQL - "trzeba się uczyć pisania zapytań na nowo" EntityManager em = getentitymanager(); CriteriaBuilder cb = em.getcriteriabuilder(); Root<Employee> r = cq.from(employee.class); cq.where(cb.equal(r.get(employee_.address).get(address_.city), "Ottawa")); cq.select(r); TypedQuery<Employee> q = em.createquery(cq); List<Employee> employees = q.getresultlist(); tymczasem to samo w JPQL: EntityManager em = getentitymanager(); Query query = em.createquery( "select e from Employee e where e.address.city = :city"); query.setparameter("city", "Ottawa"); List<Employee> employees = query.getresultlist();
Criteria API - przykład 2 CriteriaBuilder cb = getentitymanager().getcriteriabuilder(); CriteriaQuery<Order> cq = cb.createquery(order.class); Root<Order> r = cq.from(order.class); Predicate predicate = cb.and( cb.equal(r.get(order_.shipcity), shipcity), cb.equal(r.get(order_.freight), freight), cb.equal(r.get(order_.shipaddress), shipaddress)); if (countries.size() > 0) { predicate = cb.and(r.get(order_.shipcountry).in(arrays.aslist(countries)), predicate); cq.select(r).where(cb.and(cb.equal(r.get(order_.date), date), cb.equal(r.get(order_.employee).get(employee_.lastname), emplastname), cb.between(r.get(order_.orderdate), Util.dayBegin(dateFrom), Utils.dayEnd(dateTo)), cb.notequal(r.get(order_.cancelled), true), predicate )); return getentitymanager().createquery(cq).getresultlist();
Criteria API - Metamodel API EntityManager em = getentitymanager(); CriteriaBuilder cb = em.getcriteriabuilder(); Root<Employee> r = cq.from(employee.class); cq.where(cb.equal(r.get(employee_.address).get(address_.city), "Ottawa")); cq.select(r); TypedQuery<Employee> q = em.createquery(cq); List<Employee> employees = q.getresultlist(); przykładowy Metamodel: @Static Metamodel(Employee.class) public class Employee_ { public static volatile SingularAttribute<Employee, Long> id; public static volatile SingularAttribute<Employee, String> firstname; public static volatile SingularAttribute<Employee, String> lastname; public static volatile SingularAttribute<Employee, Address> address;
Criteria API - można i bez metamodelu Typesafe Criteria Query - potrzebny metamodel: EntityManager em = getentitymanager(); CriteriaBuilder cb = em.getcriteriabuilder(); Root<Employee> r = cq.from(employee.class); cq.where(cb.equal(r.get(employee_.address).get(address_.city), "Ottawa")); cq.select(r); TypedQuery<Employee> q = em.createquery(cq); List<Employee> employees = q.getresultlist(); String-based Criteria Query - bez metamodelu: EntityManager em = getentitymanager(); CriteriaBuilder cb = em.getcriteriabuilder(); Root<Employee> r = cq.from(employee.class); cq.where(cb.equal(r.get("address").get("city"), "Ottawa")); cq.select(r); TypedQuery<Employee> q = em.createquery(cq); List<Employee> employees = q.getresultlist();
Criteria API - co co się tak męczyć? sprawdzanie typów na etapie kompilacji sprawdzanie "literówek" w nazwach pól na etapie kompilacji "No to się zbudowało, to teraz zobaczmy czy działa" całkowita ochrona przed SQL Injection wydajniejsze od JPQL (?)
JPA 2.0 (10 grudnia 2009) wzparcie dla odwzorowywania kolekcji typów użytkownika wsparcie dla odwzorowywania map Embedded uporządkowane Listy Orphan removal Pessimistic Locking wzbogacone API EntityMangera Cache API Criteria API & Metamodel API wzbogacenia JPQL
Hibernate framework - JPA provider pierwsza wersja już w 2001 - JPA (2006) i konkurencja w powijakach HQL (realizacja JPQL) Criteria Queries (pierwowzór Criteria API z JPA) rozbudowania, np: Envers zaawansowane opcje sterowania wydajnością, np. Cachem
Spring Data - po co? IoC (DI) container w przykładach powyżej getentitymanager() - ale jak tę metodę napisać? wysoka testowalność (mockowanie) szablony dzięki którym piszemy mniej kodu zarządzanie transakcjami użyty przez nas: Spring ORM with plain JPA
Praktyka
Ciekawsze fragmenty - obsługa transakcji przez Springa @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW) Person save(person person);
Ciekawsze fragmenty - usuwanie public void delete(person person) { em.remove(em.merge(person));
Ciekawsze fragmenty - usuwanie z listy public Person deleteaddress(integer id, Integer addressid) { Person person = findpersonbyid(id); for (Address a : person.getaddresses()) { if (a.getid().equals(addressid)) { em.remove(em.merge(a)); person.getaddresses().remove(a); break; return person;
Ciekawsze fragmenty - definiowanie list klasa Person : @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL) @JoinColumn(name = "PERSON_ID", nullable = false) public Set<Address> getaddresses() { return addresses;
Ciekawsze fragmenty - konfiguracja JPA <properties> <property name="hibernate.dialect" value="org.hibernate.dialect. PostgresPlusDialect"/> <property name="hibernate.show_sql" value="false"/> <property name="hibernate.format_sql" value="true"/> <property name="hibernate.hbm2ddl.auto" value="create"/> <property name="hibernate.ejb.naming_strategy" value="org. hibernate.cfg.ejb3namingstrategy"/> <property name="hibernate.cache.provider_class" value="org. hibernate.cache.ehcacheprovider"/> </properties>
Źródła PostgreSQL historia, architektura: http://www.postgresql.org/docs/9.2/static/preface.html JPA: http://docs.oracle.com/javaee/6/tutorial/doc/bnbpy. html Criteria: http://www.objectdb.com/java/jpa/query/criteria/ Hibernate: http://docs.jboss.org/hibernate/orm/4.1/manual/en- US/html/ Spring: http://static.springsource.org/spring/docs/3.0.x/springframework-reference/html/orm.html http://www.mkyong.com/tutorials/spring-tutorials/