Programowanie w języku Java WYKŁAD dr inż. Piotr Zabawa Certyfikowany Konsultant IBM/Rational e-mail: pzabawa@pk.edu.pl www: http://www.pk.edu.pl/~pzabawa 28.04.2014
WYKŁAD 9 Trwałość w Java cz. 2 wykład o Hibernate został w znacznej części oparty o materiały nieznanego autora http://www.icis.pcz.pl/~draco/hibernate.pdf
Trwałość w Java Czym kierować się przy wyborze podejścia do trwałości? dr inż..piotr Zabawa Instytut Informatyki Wydział Fizyki, Matematyki i Informatyki
Trwałość w Java Argumenty za ORM Eliminuje ręczne mapowanie w Java z SQL ResultSet do klas Java. Redukuje ilość pracy wymaganej do obsługi zmiany modelu danych w relacyjnej bazie danych lub po stronie obiektowej w apliakcji. Maksymalizuje wykorzystanie dużej biblioteki trwałości pozwalając na uniknięcie rozwijania rozwiązań problemów już rozwiązanych przez innych. Eliminuje niskopoziomowy kod JDBC i SQL. Maksymalizuje możliwość wykorzystania programowania obiektowego i modelu obiektowego. Wprowadza niezależność pomiędzy bazą danych i jej strukturą (?) Provides database and schema independence. Większość produktów ORM jest darmowa i open source owa. Wiele korporacji dostarcza wsparcia i usług dla produktów ORM. Dostarcza wysoko wydajnościowych cech, jak cache owanie oraz wyrafinowana optymalizacja operacji na bazie danych. dr inż..piotr Zabawa Instytut Informatyki Wydział Fizyki, Matematyki i Informatyki
Trwałość w Java Argumenty za JPA Jest stadardem i częścią EJB3 oraz Java EE. Wiele darmowych i open source owych produktów ze wsparciem biznesowym. Przenośność pomiędzy serwerami aplikacyjnymi i produktami trwałościowymi (uniknięcie zależności od dostawcy). Użyteczna i funkcjonalna specyfikacja. Wparcie zarówno dla Java EE jak i dla Java SE. dr inż..piotr Zabawa Instytut Informatyki Wydział Fizyki, Matematyki i Informatyki
Trwałość Uwzględnić mapowania: ORM (JPA, Hibernate) GRM XML projekt Spring Data Kwestia obiektowych baz danych.
Trwałość Uwzględnić architektoniczne wzorce korporacyjne: DAO (Data Access Object) DTO (Data Transfer Object) ActiveRecord Broker SDO = JDO + DTO (Service Data Object= Java Data Object+ Data Transfer Object)
Trwałość w Java Java 8 JDBC a Java 8 http://java.dzone.com/articles/adding-java-8-lambda-goodness Nawiązać do kwestii obsługi wyjątków w Java 8 w kontekście JDBC http://openjdk.java.net/projects/jdk8/features http://blog.jooq.org/tag/java-8/ http://www.oracle.com/technetwork/java/javase/tech/index-jsp- 136101.html dr inż..piotr Zabawa Instytut Informatyki Wydział Fizyki, Matematyki i Informatyki
Trwałość w Java Natura problemów z dopasowaniem relacyjnych baz danych do paradygmatu obiektowego: Celem optymalizacji relacyjnych baz danych (normalizacja) jest oszczędność zasobów poprzez unikanie redundancji Celem optymalizacji kodu obiektowego jest czytelność i łatwość wprowadzania zmian Dlatego zestawienie tych dwóch światów jest trudne jest konflikt interesów. dr inż..piotr Zabawa Instytut Informatyki Wydział Fizyki, Matematyki i Informatyki
Trwałość w Java Charakter niedopasowania: Granulacja inna ilość klas niż tabel Dziedziczenie nie wspierane bezpośrednio w RDB Asocjacje jednokierunkowe w Java, klucze obce w RDB Krotność asocjacji nie jest specyfikowana wprost w Java, wspierana przez klucze obce w RDB Nawigowanie po danych przechodzenie po obiektach w Java, łączenie tabel (join) w RDB dr inż..piotr Zabawa Instytut Informatyki Wydział Fizyki, Matematyki i Informatyki
JPA 2.1
JPA 2.1 JPA stanowi specyfikację mapowania obiektowo-relacyjnego i trwałości dedykowaną dla języka programowania Java. JPA 2.1 jest standardem opracowanym przez twórców języka Java w ramach inicjatywy standaryzacyjnej Java Community Process o identyfikatorze JSR 338. Wersja ta jest najnowszą, zatwierdzoną w 2013 roku, wersją standardu JPA obecnie wspieraną jedynie przez następujące produkty: Hibernate EclipseLink Data Nucleus JPA wraz z produktami stanowiącymi implementację tego standardu są łącznie nazywane ORM (Object-Relational Mapping).
JPA 2.1 ORM jest obecnie najbardziej dojrzałą koncepcją wiązania danych z relacyjnej bazy danych z obiektami w paradygmacie klasowoobiektowym. Dlatego podejście to oraz jego implementacja zostały przedstawione w ramach wykładu z Java SE. Zwykle ORM prezentuje się w ramach wykładów dopiero w kontekście Java EE, co wymaga uprzedniego zaznajomienia się z rozbudowanym systemem pojęć typowym dla Java EE. Jest to jednak całkowicie zbyteczne i stanowi zbędny balast dla osób pragnących poznać jedynie kwestię ORM. W ramach ORM wykorzystuje się adnotacje w celu powiązania klas z tabelami.
JPA 2.1 Potencjalne korzyści ze stosowania JPA: Mniej kodu Spójny model interakcji z bazą danych Wydajność Niezależność od wersji i od dostawcy serwera bazy danych (pod warunkiem nie korzystania ze specyfiki) Uniknięcie znajomości SQL
JPA 2.1 - wady Złożoność Trudniejsze do nauki Trudniejsze do debugowania Częstsza degradacja wydajnościowa Ograniczona elastyczność Trudniejsze uwzględnianie specyfiki serwerów bazodanowych
JPA 2.1 Przykład: @Entity @Table(name="USER") public class User { @Id @SequenceGenerator(name="UserSequence", sequencename="user_pk", allocationsize=1) @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="usersequence") private Long id; @Column(name="USERNAME", nullable=false) private String name; @Column(name="FULL_NAME") private String fullname; }
JPA 2.1 Podstawowy interfejs przy pracy z JPA, to EntityManager: EntityManager entitymanager = entitymanagerfactory.createentitymanager(); Example example = entitymanager.find(example.class, 1L); entitymanager.close();
JPA 2.1 Adnotacje dotyczące relacji: @OneToOne @OneToMany @ManyToOne @ManyToMany
JPA 2.1 Przykład: @Entity @Table(name="ORDER") public class Order { } @ManyToOne @JoinColumn(name= CUSTOMER_ID, referencedcolumnname= ID ) private Customer customer; @Entity @Table(name="CUSTOMER") public class Customer { @OneToMany(mappedBy= customer ) private Set<Order> orders; }
JPA 2.1 Gdy JPA ładuje (fetch) encję z bazy musi wiedzieć czy załadować również encje powiązane z nią relacjami. Eager: pobranie powiązanych encji od razu, domyślne dla relacji @OneToOne i @ManyToOne Lazy: pobranie powiązanych encji w chwili sięgnięcia do nich w kodzie źródłowy, domyślne dla relacji @OneToMany i @ManyToMany @OneToMany(mappedBy= customer, fetch=fetchtype.eager) private Set<Order> orders;
JPA 2.1 Uprzednie zamknięcie obiektu EtityManager przed odwołaniem do powiązanych encji prowadzi do błędu Lazy Initialization Error.
Hibernate
Hibernate Hibernate stanowi jedną z kilku implementacji standardu JPA 2.1. Jako produkt tworzony i udostępniany jest przez organizację JBoss, która należy do firmy RedHat: http://hibernate.org/orm/
Hibernate Ze względu na fakt przedstawienia w ramach zajęć laboratoryjnych narzędzia Maven podano poniżej sposób wykorzystania Hibernate z poziomu Maven a. Repozytoria Mavena proszę sprawdzić sposób nawigowania po nich! JBoss Nexus (producenta, więc pierwsze): https://repository.jboss.org/nexus/content/groups/public/org/hibernate Maven Central (z niewielkim, kilkudniowym opóźnieniem): http://repo1.maven.org/maven2/org/hibernate/hibernate-core http://mvnrepository.com/artifact/org.hibernate SourceForge: https://sourceforge.net/projects/hibernate/files/hibernate4
Hibernate Zależność Hibernate jako produktu dla Mavena (od aktualnej wersji): <dependency> <groupid>org.hibernate</groupid> <artifactid>hibernate-core</artifactid> <version>4.3.5.final</version> </dependency>
Hibernate Zależność implementacji JPA jako części Hibernate dla Mavena (od aktualnej wersji): <dependency> <groupid>org.hibernate</groupid> <artifactid>hibernate-entitymanager</artifactid> <version>4.3.5.final</version> </dependency>
Hibernate W czasie używania nowych technologii należy mieć na względzie następujące okoliczności powodujące, że nie należy używać w projektach nie służących jedynie samokształceniu wersji najnowszych oprogramowania: Brak wsparcia przez IDE (przykładem jest brak należytego wsparcia w nowej technologii Java 8 w dostępnych obecnie środowiskach Eclipse) Brak dokumentacji dla produktów open-source (czasmi i niektórych) warto korzystać z dokumentacji do tego produktu, którego się używa w projekcie Zbyt nowe IDE brak wsparcia dla wielu innych technologii Brak wsparcia ze strony społeczności zbyt mało wykrytych i rozwiązanych problemów, których wielość jest typowa dla nowych technologii
Hibernate Hiernate jest jednym z najlepiej ocenianych frameworków do realizacji warstwy dostępu do danych (ang. persistance layer = warstwa trwałości). Zapewnia on przede wszystkim odwzorowanie/translację (ang. O/R mapping) danych pomiędzy relacyjną bazą danych a światem obiektowym. Opiera się na wykorzystaniu opisu struktury danych za pomocą języka XML, dzięki czemu można "rzutować" obiekty, stosowane w obiektowych językach programowania, takich jak Java bezpośrednio na istniejące tabele bazy danych. Zawiera też mechanizm pozwalający na inżynierię w przód (ang. forward engineering) struktury bazy danych (tabel i relacji) na podstawie klas oraz inżynierię wsteczną (ang. reverse engineering) polegającą na generowaniu kodu źródłowego klas z bazy danych o określonej strukturze.
Hibernate Dodatkowo Hibernate zwiększa wydajność operacji na bazie danych dzięki buforowaniu i minimalizacji ilości przesyłanych zapytań (por. z trybem pracy wsadowej JDBC). Jest to projekt rozwijany jako open source. Według dokumentacji celem Hibernate jest zwolnienie projektanta aplikacji z konieczności ręcznego kodowania 95% zadań związanych z zapewnieniem trwałości danych. Jedną z istotnych zalet tego rozwiązania jest zwolnienie programisty z konieczności przepisywania poszczególnych pól obiektów na pola rekordu i na odwrót.
Hibernate Struktura biblioteki: Hibernate Core - podstawowa część biblioteki, wykonująca większość pracy, Hibernate Annotations - element pozwalający zastąpić większą część konfiguracji Hibernate, budowanej do tej pory w XML, poprzez adnotacje w kodzie napisane z użyciem Javy 5. Hibernate Tools które zawierają rozmaite wtyczki (plug-ins) dla Anta oraz darmowego środowiska Eclipse IDE. Wtyczka Hibernate Tools dla Eclipse oferuje: Edytor odwzorowań (uzupełnianie i kolorowanie kodu) Konsolę Hibernate (przegląd konfiguracji, klas, itp. oraz interaktywne zapytania HQL Kreatory i generatory kodu (w tym kompletny reverse-engineering istniejącego schematu bazy danych)
Architektura ogólna Hibernate: Hibernate
Hibernate Architektura szczegółowa Hibernate została przedstawiona na następnym slajdzie
Hibernate
Hibernate Plik konfiguracyjny Hibernate może mieć jedną z dwóch postaci: Plik properties o nazwie hibernate.properties (mniej wygodne) Plik XML o nazwie hibernate.cfg.xml W czasie obsługi trwałości za pomocą mapowania obiektoworelacyjnego Hibernate wykorzystuje te informacje.
Hibernate interfejsy Interfejsy podstawowe Interfejs Session główny interfejs wykorzystywany w każdej aplikacji. Jest to lekki obiekt, często tworzony i zwalniany. Stanowi formę pośrednią pomiędzy połączeniem i transakcją. Nazywa się go czasem zarządcą trwałości. Interfejs SessionFactory po jednej instacji obiektu dla każdej bazy danych. Służy do tworzenia obiektów klasy Session. Interfejs Configuration służy do konfiguracji i uruchamiania Hibernate. Interfejs Transaction pozwala na wykorzystywanie mechanizmu transakcji i jego użycie jest opcjonalne. Interfejsy Query i Criteria służą do wysyłania zapytań i sterowania procesem ich wykonania. Można wykorzystać język HQL (Hibernate Query Language) lub natywny dla bazy danych SQL.
Hibernate interfejsy Interfejsy wywołań zwrotnych (ang. callback) Opcjonalne interfejsy umożliwiające aplikacji otrzymywanie powiadomień o zmianie stanu obiektu. Przykładem zastosowań są operacje audytowe (zapisywanie w bazie danych informacji o aktywnościach systemu softwerowego). Interfejsy Lifecycle i Validator rekacja obiektó trwałych na operacje CRUD Interfejs Interceptor oparty o koncepcję odwróconego sterowania (inversion of control) interfejs pozwalający uniknąć w obsłudze wywołań zwrotnych konieczności implementowania interfejsów Hibernate po stronie aplikacji
Hibernate stany obiektów Z perspektywy Hibernate można wyróżnić następujące trzy stany: Transient (ulotny) są to obiekty klas aplikacji nie odwzorowywanych w bazę danych, czyli takie, które nie są trwałe Persistent (trwały) są to trwałe obiekty klas encyjnych. Obiekt staje się trwały po wywołaniu na nim operacji save() z interfejsu Session następuje wtedy powiązanie go z określoną sesją. Egzemplarz obiektu trwałego może też powstać na podstawie informacji z bazy danych uzyskanych w wyniku wykonania zapytania, wyszukania identyfikatora lub przejścia do niego przez graf obiektów z innego obiektu.obiekt trwały może stać się ulotnym po wykonaniu na nim operacji delete() z interfejsu Session wtedy wiersz reprezentujący go zostaje usunięty z tabeli a obiekt odłączony od sesji. Detached (odłączony) obiekt przechodzi w ten stan po zamknięciu jego sesji za pomocą operacji close() obiektu Session. Możliwość ponownego podłączenia do sesji obiektó uzyskanych uprzednio z innej sesji w znaczącym stopniu wpływa na sposób projektowania aplikacji wielowarstwowych. Możliwość zwrócenia obiektów z jednej transakcji do warstwy wyższej (np. warstwy prezentacji) i ponowne użycie ich w innej transakcji stanowi jeden z głównych powodów popularności Hibernate. Wszystkie obiekty pobrane w trakcie trwania transakcji są niejawnie odłączane w momencie zamykania sesji lub w momencie ich serializacji (na przykład w celu przesłania do innego komputera).
Hibernate - scenariusze Przykładowy scenariusz uczynienia obiektu trwałym (zapis): SessionFactory sf = new Configuration().configure().buildSessionFactory(); Session s = sf.opensession(); Transaction tx = s.begintransaction(); Osoba o= new Osoba(); o.setimie("zbigniew"); o.setnazwisko("boniek"); s.save(o); tx.commit(); // możliwe jest wycofanie transakcji: tx.rollback()) s.close();
Hibernate - scenariusze Przykładowy scenariusz pobrania obiektu z bazy (odczyt): //Wyjątek gdy błędny klucz: Osoba o = (Osoba) s.load(osoba.class, new Long(20)); System.out.println(o.getImie()); albo: // Zwraca null gdy błędny klucz Osoba o = (Osoba) s.get(osoba.class, new Long(20)); System.out.println(o.getImie());
Hibernate - scenariusze Przykładowy scenariusz usunięcia obiektu z bazy (zapis): // obiekt stanie się ulotny,ale referencja może zostać: s.delete(o);
Hibernate - scenariusze Przykładowy scenariusz modyfikacji obiektu trwałego (zapis): o.setnazwisko("smolarek"); // jeśli zmiany mają natychmiast zostać zapisane w bazie: s.flush();
Hibernate Reguły POJO (najdogodniejsze) dla Hibernate są następujące: Metody set/get (accessor/mutator)(metody dostępowe) dla trwałych atrybutów klasy pól w tabeli, dzięki którym mamy dostęp do prywatnych pól klasy Bezargumentowy konstruktor (może być domyślny)-jest wymagany przez Hibernate do tego, aby klasa mogła być utrwalana w bazie. Identyfikator (opcjonalnie) Oprócz domyślnego konstruktora dla każdej klasy, możemy dostarczyć także konstruktor umożliwiający bezpośrednie przypisanie pól innych niż klucze podstawowe. Dzięki temu możemy stworzyć i wypełnić obiekt w jednej czynności zamiast kilku. Klasa nie może być zadeklarowana jako final Może być final, ale w pewnych przypadkach może to np. ograniczać możliwości strojenia wydajności Hibernate może wewnętrznie zarządzać identyfikatorami obiektu (ale jest to niezalecane)
Hibernate prosty przykład Klasa POJO bez adnotacji: public class Osoba { private Long id; private String imie; private String nazwisko; public Osoba() {} public void setid(long id) { this.id = id; }
Hibernate prosty przykład } public Long getld() { return id; } public void setimie(string imie) { this.imie = imie; } public String getimie() { return imie; } public void setnazwisko(string nazwisko) { this. nazwisko = nazwisko; } public String getnazwisko () { return nazwisko; }
Hibernate prosty przykład Klasa POJO z adnotacjami: @Entity @Table(name = "Osoba") public class Osoba { @Id @GeneratedValue @Column(name = "Num_osob") private Long id; @Column(name = "Imie") private String imie; @Column(name = "Nazwisko") private String nazwisko; }
Hibernate zapytania HQL Zapytania w języku HQL jest to język zorientowany obiektowo o składni podobnej do SQL odwołujący się do klas a nie do tabel. wykonanie zapytań poprzez list() - zwraca cały wynik zapytania do kolekcji w pamięci (instancje pozostają w stanie trwałym): List osoby= (List) session.createquery("from Osoba as osoba where osoba.imie= 'Zbigniew'").list(); for (int i=0; i<osoby.size(); i++) System.out.println(((Osoba)osoby.get(i)).getImie()); List osoby = (List)s.createQuery("select Osoba.id, Osoba.imie from Osoba as osoba where Osoba.nazwisko = 'Boniek'").list(); for (int i=0; i<osoby.size(); i++) { } Object [ ] tab = (Object [ ]) osoby.get(i); System.out.println(tab[0]+" "+tab[1]);
Hibernate zapytania wykonywanie zapytań poprzez iterate() - zwraca wynik w kilku zapytaniach SELECT: może być efektywniejsze od list(), gdy instancje już są w pamięci podręcznej, ale rozwiązanie to jest zazwyczaj wolniejsze. Iterator osoby = s.createquery("from Osoba as osoba where osoba.nazwisko = 'Boniek'").iterate(); while (osoby.hasnext()) { } Osoba o = (Osoba) osoby.next(); System.out.println(o.getImie());
Hibernate zapytania Pobieranie obiektów z bazy danych w przypadku zapytań join zawierających odniesienia do tabel połączonych relacjami: @OneToMany i @ManyToMany następuje leniwe pobieranie (ang. lazy fetching) rekordów z bazy w czasie pierwszego odwołania do nich @OnetoOne oraz @ManytoOne następuje natychmiastowe pobieranie (ang. Eager fetching) rekordów z bazy Oba powyższe zachowania są domyślne.
Hibernate zapytania SQL Zapytania w natywnym języku SQL dają możliwość wykorzystania specyficznych dla danego systemu konstrukcji składniowych np. CONNECT w Oracle.
Hibernate zapytania Criteria Zapytania poprzez obiekty klasy Criteria umożliwiają budowę zapytań poprzez obiektowe API
Hibernate zapytania Example Zapytania poprzez obiekty klasy Example umożliwiają wyszukiwanie danych w oparciu o przykadową instancję (mechanizm QBE QueryByExample)
Hibernate dobre praktyki Źródło: http://blog.castsoftware.com/using-hibernate-frameworks-what-arethe-best-practices/ Tworzyć wiele małych klas i odwzorowywać je (ang. map) w <component> Dla klas trwałych deklarować własności (ang. properties ) identyfikatora Identyfikować naturalne klucze Umieszczać każde odwzorowanie klasy w jej własnym pliku Ładować odwzorowania jako zasoby Rozważać eksternalizowanie stringów zapytań Nie zarządzać własnymi połączeniami JDBC Rozważać używanie własnych typów Używać ręcznie zakodowanych odwołań do JDBC w wąskich gardłach aplikacji Rozumieć flushing obiektów klasy Session
Hibernate dobre praktyki Rozważyć używanie obiektów odłączonych w aplikacjach 3-warstwowych Rozważyć używanie długich kontekstów trwałości w aplikacjach 2- warstwowych Nie traktować wyjątków jako recoverable Preferować lazy fetching dla asocjacji W ramach MVC używać otwartych sesji albo disciplined assembly phase aby uniknąć problemów z unfetched data Rozważyć uniezależnienie logiki biznesowej od Hibernate Nie używać egzotycznych odwzorowań asocjacji Preferować asocjacje dwukierunkowe UWAGA: wielość dobrych praktyk wynika z braku określenia kryterium jakości nie wiemy co kto optymalizuje ani jak zmierzyć jakość zestawu praktyk!
Hibernate Przykład wykorzystania Hibernate w aplikacji Java SE p. Klęk. W tym przykładowy plik pom
Hibernate Uruchamianie aplikacji z Hibernate - możliwości: Uzyskanie połączenia z istniejącą bazą danych przy starcie lub w trakcie działania Utworzenie bazy danych przy starcie aplikacji kwestia zainicjowania jej danymi Możliwość wykorzystania języka DDL do utworzenia bazy danych
Hibernate Praca z Hibernate w środowisku IDE na przykładzie Eclipse a.
Hibernate Materiały dodatkowe O wydajności JPA http://www.bottega.com.pl/pdf/materialy/jpa-wydajnosc.pdf
Koniec