Technologie dostępu do baz danych z aplikacji Javy:

advertisement
Technologie dostępu do baz danych z aplikacji Javy:
●
JDBC – (ang. Java DataBase Connectivity - łącze do baz danych w języku Java) - interfejs
programowania opracowany w 1996 r. przez Sun Microsystems, umożliwiający
niezależnym od platformy aplikacjom napisanym w języku Java porozumiewać się z bazami
danych za pomocą języka SQL.Jest to podstawowy interfejs dostępu do baz danych – lecz
ręczne kodowanie w JDBC jest uciążliwe i podatne na błędy
●
SQLJ – jest metodą osadzania instrukcji SQL w kodzie JAVA, które następnie przy użyciu
translatora SQLJ zamieniane są na kod JAVA– Jest to odporna na błędy alternatywa dla
bezpośredniego korzystania z JDBC
●
Encyjne EJB (Enterprise JavaBeans)– reprezentują w sposób obiektowy dane (np.
przykrywają relacyjną bazę danych).Wszelki dostęp do danych (w tym i masowy) odbywa
się poprzez obiekty napisane w języku Java, a zatem niesie ze sobą ograniczenia
wydajnościowe i zajętości zasobów.Ziarenka encyjne mimo ich konkretnych zadań
określonych w specyfikacji EJB, w praktyce nie są przenośne.Nie poddają się również
serializacji,przez co trzeba definiować dodatkowe obiekty transferu danych (DTO – Data
Transfer Object).EJB wymusza nienaturalny styl programowania w Javie i utrudnia
ponowne wykorzystanie kodu poza konkretnym kontenerem. Obecnie trwają prace nad
przebudowaną wersją 3.0 specyfikacji
●
Biblioteka znaczników JSTL SQL – Wygodna w prostych aplikacjach opartych na JSP –
Miesza logikę biznesową z logiką prezentacji, narusza model MVC
MVC (ang. Model-View-Controller) - Model-Widok-Kontroler to wzorzec projektowy
w informatyce, którego głównym założeniem jest wyodrębnienie trzech podstawowych
komponentów aplikacji:
• modelu danych
• interfejsu użytkownika
• logiki sterowania
w taki sposób, aby modyfikacje jednego komponentu minimalnie wpływały na
pozostałe.
●
Technologie odwzorowania obiektowo-relacyjnego (ang. O/RM (ORM) – object/relational
mapping)– Efektywnie mapują świat obiektów na świat relacyjnej bazy danych.Do
podstawowych zalet O/RM należą:
– Produktywność (uniknięcie tworzenia uciążliwego kodu obsługującego bazę danych)
– Łatwość pielęgnacji aplikacji (mniej kodu, elastyczność)
– Wydajność (przy założeniu ograniczonego czasu i budżetu na zbudowanie i
optymalizację aplikacji)
– Niezależność od konkretnego SZBD
Technologie te najczęściej działają w warstwie webowej aplikacji. W chwili obecnej
stanowią najlepsze rozwiązanie.
Rozwiązanie ORM składa się z 4 podstawowych elementów:
● interfejsu do przeprowadzania podstawowych operacji CRUD na obiektach klas
zapewniających trwałość
● języka lub interfejsu programistycznegodo określania zapytań związanych z klasami
lub ich właściwościami
● narzędzia do określania metadanych
● technik interakcji z obiektami trwałymi m.in.
– Wykrywanie zmian w trwałych obiektach
– Różne sposoby pobierania obiektów powiązanych asocjacjami
– Techniki optymalizacji
Popularne implementacje technologii odwzorowania obiektowo-relacyjnego dla aplikacji Java:
● Hibernate (Open Source)
● Oracle Toplink (rozwiązanie firmowe Oracle)
● JDO (Java Data Objects - specyfikacja firmy Sun)
● JPA (Java Persistence API)
O Hibernate
Hibernate - Jeden z najlepiej ocenianych frameworków do realizacji warstwy dostępu do danych
(ang. persistance layer). Zapewnia on przede wszystkim 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. Dodatkowo Hibernate zwiększa wydajność operacji na bazie danych dzięki buforowaniu i
minimalizacji liczby przesyłanych zapytań. Jest to projekt rozwijany jako open source. Głównym
inicjatorem i liderem projektu jest Gavin King. 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. Hibernate jest najbardziej odpowiedni dla aplikacji Java
pracujących w warstwie pośredniej
Hibernate jest tworzony przez firmę Jboss. Możemy go pobrać ze strony http://www.hibernate.org.
Dystrybucja zawiera bibliotekę Hibernate i wykorzystywane przez nią biblioteki od innych
dostawców (znajdujące się w katalogu /lib).Hibernate został podzielony na kilka części:
•
•
•
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)
Zalety Hibernate:
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
Najpopularniejsza obecnie implementacja odwzorowania obiektowo-relacyjnego dla
Javy(13 000 pobrań/miesiąc)
– Dużo materiałów i pomocy w Internecie
– Udział twórców Hibernate w pracach nad specyfikacją EJB 3.0
Open source (Rozpowszechniany na licencji FSF Lesser GNU Public License)
Wsparcie dla stylu programowania odpowiedniego dla Javy
(Hibernate: Relational Persistence For Idiomatic Java)
– Obsługa asocjacji, kompozycji, dziedziczenia, polimorfizmu, kolekcji
Wysoka wydajność i skalowalność – Dual-layer cache, możliwość wykorzystania w klastrze –
UPDATE tylko dla zmodyfikowanych obiektów i kolumn
Własne API
Możliwość stworzenia dużej bazy bez większego wkładu pracy
Pozwala uniknąć ręcznego zarządzania danymi na poziomie SQL i JDBC
Będzie wykorzystane w JBoss CMP 2.0 engine
Nie ma potrzeby implementowania interfejsów
Korzystamy z możliwości SQL bez wychodzenia poza granicę języka Java
Wiele sposobów wydawania zapytań:
– HQL – własne przenaszalne rozszerzenie SQL
– Natywny SQL
– Zapytania przez obiekty Javy: Criteria i Example
Dowolna klasa może być klasą reprezentującą encję
Wygodne i intuicyjne mapowanie plików struktury XML na klasy
Mniej kodu = mniej błędów
Optymalna wydajność
Trwałość dla JavaBeans
Wsparcie dla bardzo rozwiniętych modeli obiektowych
Rozbudowane zapytania
Jeden krok dalej od zwykłego wykorzystania JDBC
Ogólna architektura Hibernate:
Hibernate stanowi warstwę abstrakcji pośredniczącą w komunikacji aplikacji z bazą danych,
obsługując trwłaość obiektów aplikacji.Opcje konfiguracyjne Hibernate, takie jak np. parametry
połączenia z bazą danych zawiera plik konfiguracyjny Hibernate, który może mieć postać pliku
.properties (o nazwie hibernate.properties) lub, co jest wygodniejsze, postać pliku XML (o nazwie
hibernate.cfg.xml).Obsługując trwałość obiektów,Hibernate wykorzystuje informacje o
odwzorowaniu obiektowo-relacyjnym modelu danych aplikacji zawarte w plikach XML.
Szczegółowa architektura Hibernate:
Interfejsy Hibernate w poszczególnych warstwach:
Interfejsy Hibernate przedstawione na rysunku można podzielić następująco:
-
Interfejsy wywoływane przez aplikacje w celu wykonania prostych operacji CRUD i
wyszukiwania. Interfejsy te stanowią główny punkt styku logiki biznesowej z Hibernate.
Zawierają interfejsy: Session, Transaction i Query.
Interfejsy wywoływane przez kod infrastruktury aplikacji w celu konfiguracji Hibernate. Jest to
przede wszystkim klasa Configuration.
Interfejsy wywołań zwrotnych pozwalające aplikacji reagować na zdarzenia zachodzące
wewnątrz Hibernate. Są to interfejsy: Interceptor, Lifecycle i Validatable.
Interfejsy zapewniające rozszerzanie rozbudowanej funkcjonalności odwzorowania w
Hibernate. Są to interfejsy: UserType, CompositeUserType i IdentifierGenerator. Interfejsy te
implementuje kod infrastruktury aplikacji (jeśli to konieczne).
Hibernate wykorzystuje istniejące interfejsy programistyczne Javy, włączając w to JDBC, JTA
(Java Transaction API) oraz JNDI (Java Naming and Directory Interface). JDBC zapewnia
odpowiedni poziom abstrakcji funkcji wspólnych dla różnych systemów relacyjnych baz danych, co
oznacza, że Hibernate potrafi skorzystać z dowolnej bazy danych ze sterownikiem JDBC.
Interfejsy podstawowe
Pięć interfejsów podstawowych pojawia się w niemal każdej aplikacji wykorzystującej Hibernate.
Dzięki nim możliwe staje się pobieranie i zapamiętywanie trwałych obiektów oraz sterowanie
transakcjami.
Interfejs Session
Interfejs Session to główny interfejs każdej aplikacji Hibernate. Egzemplarze Session są lekkie —
koszt ich utworzenia i zniszczenia bywa niewielki. Jest to niezwykle istotne, bo każda aplikacja
cały czas tworzy i usuwa coraz to nowe sesje, prawdopodobnie przy każdym nowym żądaniu.
ObiektSession reprezentuje jednostkę pracy („unit of work”),jest tworzony dla pojedynczego
procesu.Znaczenie sesji Hibernate można opisać jako coś pomiędzy połączeniem i transakcją.
Najprościej traktować sesję jako bufor lub kolekcję załadowanych obiektów powiązanych z jedną
jednostką zadaniową. Hibernate potrafi wykryć zmiany w obiektach tej jednostki zadaniowej.
Często interfejs Session nazywa się zarządcą trwałości, gdyż zapewnia dostęp do podstawowych
operacji trwałości takich jak zapis i pobieranie obiektów. Warto podkreślić, że sesja Hibernate nie
ma nic wspólnego z sesją warstwy połączenia internetowego, na przykład HttpSession.
Interfejs SessionFactory
Interfejs SessionFactory został zaprojektowany z myślą o współdzieleniu przez wiele wątków
aplikacji. Najczęściej na całą aplikację występuje tylko jeden obiekt SessionFactory tworzony w
momencie jej inicjalizacji. Jeśli jednak aplikacja korzysta z kilku baz danych w połączeniu z Hibernate, potrzeba osobnego obiektu SessionFactory dla każdej bazy danych. Obiekt
SessionFactorysuży dotworzenia obiektówSession, poprzez które następnie realizowana jest
komunikacja z bazą danych. Obiekt SessionFactory buforuje wygenerowane polecenia SQL i inne
meta-dane odwzorowania stosowane przez Hibernate w trakcie działania aplikacji. Dodatkowo
buforuje dane, które zostały odczytane w jednej jednostce zadaniowej i mogłyby przydać się w
innej jednostce zadaniowej.
Interfejs Configuration
Obiekt Configuration służy do konfiguracji i uruchomienia Hibernate. Aplikacja używa
egzemplarza Configuration do wskazania położenia dokumentów odwzorowań i właściwości
specyficznych dla Hibernate.
Interfejs Transaction
Interfejs Transaction jest opcjonalny. Aplikacje Hibernate nie muszą z niego korzystać, jeśli chcą
zarządzać transakcjami we własnym zakresie. Interfejs Transaction oddziela aplikację od
implementacji transakcji ,tzn. stara się ukryć szczegóły implementacji konkretnych mechanizmów
transakcyjnych: JDBC, klasy UserTransaction z JTA.W ten sposób zapewnia aplikacji jednorodny
sposób ich obsługi. Ułatwia to zachowanie przenośności aplikacji Hibernate między różnymi
środowiskami wykonywania i kontenerami. Często w sesji występuje tylko jedna transakcja, ale
możliwe wiele kolejnych transakcji w jednej sesji. Hibernate nie pozwala na tryb auto-commit.
Interfejsy Query i Criteria
Interfejs Query umożliwia wysyłanie zapytań do bazy danych i sterowanie procesem ich
wykonywania. Zapytania pisze się w języku HQL lub też w odpowiednim dla danej bazy danych
dialekcie SQL. Egzemplarz Query odpowiada za dowiązanie parametrów zapytania, ograniczenie
liczby zwracanych wyników i wykonanie zapytania. Interfejs Criteria jest bardzo podobny.
Umożliwia utworzenie i wykonanie obiektowych kryteriów wyszukiwania. Egzemplarz Query nie
daje się zastosować poza egzemplarzem Session, dla którego powstał.
Interfejsy wywołań zwrotnych
Interfejsy wywołań zwrotnych umożliwiają aplikacji otrzymywanie powiadomień, gdy coś
ciekawego stanie się ze sprawdzanym obiektem — na przykład zostanie załadowany, zapisany lub
usunięty. Aplikacje stosujące Hibernate nie muszą korzystać z wywołań zwrotnych, ale
niejednokrotnie przydają się one do tworzenia ogólnej funkcjonalności, na przykład
automatycznego tworzenia zapisów audytorskich.
Interfejsy Lifecycle i Validatable zapewniają trwałym obiektom reakcję na zdarzenia związane z
własnym cyklem życia trwałego. Cykl życia ma nierozerwalny związek z operacjami CRUD
obiektu.
Interfejs Interceptor wprowadzono, by aplikacje mogły przetwarzać wywołania zwrotne bez
potrzeby wprowadzania implementacji interfejsów Hibernate do klas trwałości. Implementacje
interfejsu Interceptor wprowadza się do egzemplarzy trwałych klas jako parametry.
Warstwa prezentacji
Klientem może być samodzielna aplikacja napisana w Javie, instalowana na komputerze (thick
client), aplikacja zbudowana w oparciu o strony WWW z, której korzystamy poprzez przeglądarkę
internetową z dowolnego miejsca (thin client), lub aplikacja dla urządzeń mobilnych napisana w
Javie (Java 2 Micro Edition).
Warstwa biznesowa
Warstwa zawierająca całą logikę biznesową aplikacji. Umożliwia dostęp do źródeł danych,
integracje nowego systemu w technologii J2EE z istniejącymi systemami informatycznymi w
przedsiębiorstwie. Warstwa osłania system informatyczny przed warstwą prezentacji widoczną
przez klienta. Warstwa biznesowa może być niezależna od technologii w jakiej została
zrealizowana warstwa danych.
Warstwa danych
Warstwa danych zapewnia trwałość danych dla aplikacji. Trwałość może być realizowana przez
relacyjną bazę danych, system katalogowy, system plików, czy obiektową bazę danych.
Architektura wielowarstwowa znaczne upraszcza proces projektowania, tworzenia oraz wdrażania
rozwiązań biznesowych.
Kod aplikacji używa interfejsów programistycznych Session i Query z Hibernate do wykonywania
operacji trwałości danych.Zajmuje się również zarządzaniem transakcjami, używając interfejsu
Transaction z Hibernate. Obiekt zapytania jest tworzony metodą createQuery() obiektu Session.
Zatwierdzenie transakcji następuje po wywołniu metody commit() obiektu Transaction (do
wycofania transakcji służy metoda rollback()) Wyniki zapytania zostają zwrócone jako kolekcja
typu List metodą list()obiektu Query. Pula połączeń określa liczbę połączeń, jakie mogą zostać
nawiązane, dodatkowo odpowiada za zarządzanie połączeniami. Hibernate potrafi zintegrować się
z dowolną pulą połączeń, sam tworzy pule połączeń po podaniu wymaganych parametrów. Plik
konfiguracyjny informuje, w jaki sposób Hibernate może uzyskać (lub utworzyć nowe) połączenia
JDBC. Ogólnie nie zaleca się tworzenia połączeń za każdym razem, gdy tylko chce się skorzystać z
bazy danych, ponieważ uzyskiwanie nowych połączeń oraz utrzymywanie wielu
niewykorzystywanych połączeń jest kosztowne.
Stany obiektów w Hibernate
W Hibernate mamy następujące stany obiektów:
●
Transient (ulotny)
W Hibernate obiekty utworzone operatorem new nie stają się automatycznie trwałe. Ich
stan jest ulotny, czyli nie są związane z żadnym wierszem bazy danych. Znikną, gdy
tylko przestanie istnieć referencja do nich w całej aplikacji. Cykl życia obiektów tego
typu jest ściśle związany z ich dostępnością. Kończy się, gdy przestają być dostępne i
mogą zostać usunięte przez mechanizm odzyskiwania pamięci. Hibernate traktuje
wszystkie egzemplarze ulotne jako nietransakcyjne — tzn, że modyfikacja stanu takiego
obiektu nie wykonuje się w kontekście żadnej transakcji.
●
Persistent (trwały)
Obiekt trwały to dowolny egzemplarz o tożsamości bazodanowej. Obiekt trwały mógł
zostać utworzony przez aplikację, a następnie uczyniony trwałym przez wywołanie
metody save() zarządcy trwałości (obiektu Session).Po tej operacji obiekt jest związany
z sesją. Ewentualnie egzemplarz obiektu trwałego może 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. Innymi
słowy, obiekty trwałe zawsze mają związek z obiektem Session i są transakcyjne.
Hibernate wykrywa zmiany dokonane na trwałych obiektach i synchronizuje ich stan z
bazą danych na końcu transakcji. Oprogramowanie ORM musi wykrywać, które z
obiektów trwałych zmieniły się w czasie trwania transakcji. Sprawdzenie to nosi nazwę
automatycznego sprawdzania zabrudzenia (obiekt z modyfikacjami, które nie zostały
jeszcze zapisane w bazie danych, traktuje się jako brudny). W momencie
zatwierdzania transakcji stan obiektu znajdujący się w pamięci trafia do bazy danych w
wyniku wykonania polecenia INSERT, UPDATE lub DELETE. Obiekt trwały może stać
się ulotnym po wywołaniu metody delete() interfejsu zarządcy trwałości powodującej
usunięcie z bazy danych wiersza z danymi obiektu.
●
Detached (odłączony)
W skrócie można powiedzieć, że jest to obiekt, który był trwały, ale jego sesja
zakończyła się. W Hibernate w momencie wywołania metody close() obiektu Session
egzemplarze tracą połączenie z zarządcą trwałości. Obiekty te uważa się za odłączone,
gdyż nie gwarantuje się synchronizacji ich danych z informacjami w bazie danych, bo
nie są od tego momentu zarządzane przez Hibernate. Hibernate dopuszcza ponowne
użycie obiektu w nowej transakcji przez jego powiązanie z nowym zarządcą (po
przypisaniu obiekt ponownie traktuje się jako trwały). Ta cecha w znaczącym
stopniu wpływa na sposób projektowania aplikacji wielowarstwowych. Możliwość
zwrócenia obiektów z jednej transakcji do warstwy prezentacji i ponowne użycie ich w
innej transakcji stanowi jeden z głównych powodów popularności Hibernate. Hibemate
jawnie udostępnia również operację odłączenia — metodę evict() obiektu Session.
Stosuje się ją jednak głównie w zarządzaniu buforami (ze względów wydajnościowych).
Dokonanie jawnego odłączenia nie jest operacją typową. 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).
Przykłady Zapisu i odczytu obiektu do/z bazy danych:
SessionFactory sf = new Configuration().configure().buildSessionFactory();
Session s = sf.openSession();
// rozpoczęcie sesji
Transaction tx = s.beginTransaction();
// rozpoczęcie transakcji
• Uczynienie nowego obiektu trwałym:
Osoba o= new Osoba();
o.setImie("Zbigniew");
o.setNazwisko("Boniek");
s.save(o);
tx.commit();
s.close();
// zapis obiektu do bazy danych
// zatwierdzenie transakcji (wycofanie: tx.rollback())
// zakończenie sesji
• Odczyt obiektu o znanym identyfikatorze:
Osoba o = (Osoba) s.load(Osoba.class, new Long(20));
System.out.println(o.getImie());
Osoba o = (Osoba) s.get(Osoba.class, new Long(20));
System.out.println(o.getImie());
//Wyjątek gdy błędny klucz
// Zwraca null gdy błędny klucz
• Usuwanie obiektu:
s.delete(o);
// obiekt stanie się ulotny,ale referencja może zostać
• Modyfikacja trwałego obiektu (w czasie otwartej sesji Session s):
o.setNazwisko("Smolarek");
s.flush();
// jeśli zmiany mają natychmiast powędrować do bazy
Elementy Hibernate:
Trwałe klasy (Persistent classes):
● Trwałe klasy to klasy w aplikacji, które implementują encje występujące w reprezentowanej
rzeczywistości:
– Np. Osoba,Użytkownik,Klient, Faktura
– Nie wszystkie instancje trwałej klasy muszą być trwałe
public class Osoba {
private Long id;
private String imie;
private String nazwisko;
Możliwa odpowiadająca tabela w bazie danych:
public Osoba() {}
public void setId(Long id) {
this.id = id;
}
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 najlepiej działa z klasami spełniającymi reguły POJO (Plain Old Java Object)
Reguły POJO dla Hibernate są następujące:
– Metody set/get (accessor/mutator)(Metody dostępowe) dla trwałych atrybutów klasypó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)
Odwzorowanie obiektowo-relacyjne, czyli pliki mapujące (*.hbm.xml):
●
●
Odwzorowanie definiowane jest w pliku XML.W zasadzie nazwa pliku i jego
umiejscowienie w strukturze plików nie jest istotne, jednakże przyjęło się, że każda klasa,
która będzie obsługiwana przez Hibernate, powinna posiadać swój własny plik mapowania,
który znajduje się w katalogu odpowiadającym pakietowi mapowanej klasy z nazwą
odpowiadającą nazwie klasy z rozszerzeniem hbm.xml. Język do opisu odwzorowania jest
zorientowany na Javę, tzn że odwzorowanie budujemy z punktu widzenia klasy Java a nie tabeli.
Dokumenty opisujące odwzorowanie można tworzyć ręcznie lub korzystać z dostępnych
narzędzi:
- Generujących odwzorowanie z POJO (np. Xdoclet)
- Generujących POJO i odwzorowanie z tabel bazy danych na zasadzie reverse
engineering (np. Hibernate Tools)
Dokumenty opisujące odwzorowanie są wskazywane w pliku konfiguracyjnym
hibernate.cfg.xml (lub też programowo w aplikacji):
<?xml version='1.0'encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
•••
<mapping resource="hib/Osoba.hbm.xml"/> // Ścieżka do pliku z odwzorowaniem
</session-factory> </hibernate-configuration>
●
●
●
Odwzorowane klasy muszą deklarować klucz główny tabeli co jest realizowane poprzez
element <id>.Istnieje możliwość stosowania złożonych kluczy głównych z Hibernate,
odwzorowanych przez <composite-id>, ale nie jest to zalecane.Nazwa pola jest określona
poprzez atrybut name a nazwa kolumny w tabeli poprzez atrybut column.
Zalecane jest stosowanie sztucznego identyfikatora, typu nie-prostego (możliwość
przypisania null).Klucz zastępczy jest to dowolna wartość (zazwyczaj liczbowa) o typie
danych zależnym od liczby spodziewanych obiektów (np. 32-bitowy, 64-bitowy, etc.). Poza
bazą danych klucz zastępczy nic nie znaczy – nie jest to ani numer klienta, ani numer
telefonu, ani nic innego. Z tego względu, jeśli jakaś decyzja biznesowa duplikuje dotychczas
niepowtarzalne dane, nie spowoduje to problemów, ponieważ biznesowe nie tworzą klucza
podstawowego.
W definicji identyfikatora występuje element opisujący sposób generacji jego wartości
<generator>.Może on przyjmować następujące wartości:
– assigned: aplikacja musi wypełnić wartość (strategia domyślna)
– increment: generacja programowa (działa gdy jeden proces w danej chwili może
wstawiać wiersz do tabeli)
– hilo: Generuje niepowtarzalne wartości typu long wykorzystując algorytm
HIGH-LOW.
– native: wartości klucza mają być generowane domyślnym natywnym sposobem dla
wykorzystywanej bazy danych.(identity, sequence , hilo)
– identity: wsparcie dla automatycznie generowanych wartości kolumny w DB2,
MySQL, MS SQL Server, Sybase and HypersonicSQL (np. AUTO_INCREMENT w
MySQL)
– sequence: generuje niepowtarzalną wartość wykorzystując konstrukcję SEQUENCE
dostępną w bazach danych DB2, Interbase, Oracle, PostgreSQL i SAP DB.
– select: pobiera wartość ustawioną przez wyzwalacz (trigger) w bazie danych.
Możliwe typy danych w Hibernate:
– integer, long, short, float, double, character, byte, boolean (BIT), yes_no
(CHAR(1): Y/N), true_false (CHAR(1): T/F): odwzorowanie typów prostych Javy na
●
odpowiednie typy SQL (vendor-specific)
– string: odwzorowanie java.lang.String na VARCHAR (w Oracle VARCHAR2)
– date, time, timestamp: odwzorowanie java.util.Date na DATE, TIME,
TIMESTAMP
– big_decimal, big_integer: odwzorowanie z java.math.BigDecimal i
java.math.BigInteger na NUMERIC (w Oracle NUMBER)
– calendar, calendar_date, locale, timezone, currency, class, binary, text,
serializable, clob, blob.
Element property może przyjmować następujące wartości:
name="nazwa_atrybutu"
column="nazwa_kolumny"
// Nazwa kolumny w bazie danych - domyślnie = name
type="typ_danych"
update="true|false" // Domyślnie true (false dla pól wyliczeniowych)
insert="true|false"
formula="arbitrarySQLexpression"
access="field|property|ClassName"
lazy="true|false"
// włącza tryb “lazy fetching”
unique="true|false"
not-null="true|false"
// Domyślnie false
optimistic-lock="true|false"
generated="never|insert|always"
node="element-name|@attribute-name|element/@attribute|."
index="nazwa_indeksu"
unique_key="unique_key_id"
length="L"
precision="P"
scale="S"
Przykładowy plik - Osoba.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="hib">
<class name="Osoba" table="Osoba">
// Odwzorowanie klasy na tabelę
<id name="id" type="long" column="NumOsob">
// Definicja identyfikatora
<generator class="sequence"/>
<param name="sequence">osob_seq</param>
</generator>
</id>
<property name="imie"
column="Imie"
type="string"
// Nazwa kolumny w bazie danych - domyślnie = name
// Typ danych Hibernate
// Domyślnie false
// Domyślnie true (false dla pól wyliczeniowych)
not-null="true"
update="true" insert="true"/>
<property name=”nazwisko” column=”Nazwisko” type=”string”/>
</class>
</hibernate-mapping>
Konfigurację można zapisać w pliku XML bądź, za pomocą adnotacji w kodzie Java. Ten drugi sposób jest zdecydowanie
wygodniejszy, szczególnie gdy nie przewidujemy częstych zmian w schemacie tabel:
@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;
}
Adnotacja @Entity określa klasę encji.Encja(ang.entity) to lekki obiekt służący do reprezentacji
trwałych danych. Typowo encja reprezentuje tabelę z relacyjnej bazy danych, ale istnieje również
możliwość odwzorowania encji na kilka tabel. Encja definiowana jest w formie klasy encji.
Niekiedy klasa encji jest uzupełniana o klasy pomocnicze np. klasę definiującą strukturę złożonego
klucza głównego.
@Id oznacza, że pole jest kluczem głównym, @GeneratedValue informuje że poszczególne
wartości pola mają być automatycznie generowane, a @Column definiuje nazwę kolumny w tabeli
(Osoba), z którą dane pole ma być powiązane.
Rodzaje konfiguracji (hibernate.properties lub hibernate.cfg.xml):
Hibernate możemy skonfigurować na dwa sposoby:
● Plik właściwości hibernate.properties
Jeśli właściwości zadeklaruje się w pliku hibernate.properties, wystarczy umieścić ten
plik w ścieżce wyszukiwania klas aplikacji. Zostanie on automatycznie wykryty i
wczytany przez Hibernate w momencie tworzenia obiektu Configuration.
Przykładowy plik – Hibernate.properties
hibernate.connection.driver_class=org.hsqldb.jdbcDriver
hibernate.connection.url=jdbc:hsqldb:file:testdb
hibernate.connection.username= user
hibernate.connection.password= tajne
hibernate.show_sql=false
hibernate.dialect=org.hibernate.dialect.HSQLDialect
hibernate.c3p0.min_size=5
hibernate.c3p0.max_size=20
hibernate.c3p0.timeout=300
(1)
(2)
(3)
(4)
(5)
(1) Nazwa klasy Javy implementująca sterownik JDBC dla konkretnej bazy danych(klasa
Driver).Plik jar sterownika musi znajdować się w ścieżce wyszukiwania klas.
(2) Adres URL w formacie jdbc określający adres serwera i nazwę bazy danych dla połączeń JDBC
(3)Minimalna liczba oczekujących na działania połączeń JDBC utrzymywanaprzez C3P0.
(4)Maksymalna liczba połączeń w puli. Zostanie zgłoszony wyjątek wykonania,
gdy wartość ta okaże się niewystarczająca.
(5)Okres bezczynności podawany w sekundach, więc w przykładzie jest to 5 minut. Po tym okresie
nieużywane połączenie zostaje usunięte z puli.
Określanie właściwości w postaci hinernate.c3p0.* powoduje automatyczne wybranie biblioteki
C3P0 jako rozwiązania puli połączeń dla Hibernate (nie potrzeba żadnej dodatkowej opcji w celu
włączenia C3P0).
Plik ten nie zawiera odwzorowań zasobów z pliku XML – i nie można zamieścić tej informacji w
pliku właściwości. Jeżeli w ten sposób chcemy skonfigurować Hibernate, należy odwzorować
klasy do obiektu Configuration w czasie wykonania:
Configuration config = new Configuration();
config.addResource("Osoba.hbm.xml");
config.addClass( katalog.Osoba.class );
config.setProperties( System.getProperties() );
SessionFactory sessions = config.buildSessionFactory();
●
Plik konfiguracyjny hibernate.cfg.xml
Konfiguracja poprzez plik hibernate.cfg.xml jest najczęściej wykorzystywanym
sposobem. Wiele osób preferuje właśnie w ten sposób, by uniknąć dodawania
parametrów do obiektu Configuration w kodzie aplikacji. Metoda odszukiwania pliku
przez Hibernate polega na przeszukaniu CLASSPATH(ścieżki wyszukiwania klas).
Podczas budowania projektu, należy pamiętać o umieszczeniu pliku hibernate.cfg.xml
do odpowiedniego katalogu, który jest włączony do CLASSPATH.
W odróżnieniu od pliku hibernate.properties, który zawiera tylko parametry
konfiguracyjne plik hibernate.cfg.xml może również określać lokalizację plików
odwzorowań.
Na początku pliku występuje deklaracja typu dokumentu, która pomaga analizatorowi
XML dokonać walidacji dokumentu, czyli sprawdzić zgodność jego formatu z
wytycznymi zawartymi w DTD pliku konfiguracyjnego Hibernate.
Konfiguracja Hibernate jest zawarta w elemencie głównym <hibernate-configuration>.
Natomiast parametry konfiguracyjne dla konkretnej bazy danych są zawarte w
elemencie <session-factory>. Parametry połączenia z bazą danych są zawarte w
poszczególnych elementach <property>, a pliki z opisem odwzorowania klas Java na
tabele w bazie danych wskazane w elementach <mapping>.Ważnym parametrem jest
wskazanie dialektu SQL dla wykorzystywanej bazy danych (w przykładzie użyty jest
dialekt bazy danych Oracle). Ustawienie tego parametru powoduje przyjęcie
domyślnych wartości wielu innych parametrów, odpowiednich dla danej bazy danych.
Hibernate SQL Dialects (hibernate.dialect):
Nazwa:
● DB2
● DB2AS/400
● DB2OS390
● PostgreSQL
● MySQL
● MySQLwithInnoDB
● MySQLwithMyISAM
● Oracle(anyversion)
● Oracle9i/10g
● Sybase
● SybaseAnywhere
● MicrosoftSQLServer
● SAPDB
● Informix
● HypersonicSQL
● Ingres
● Progress
Dialect:
org.hibernate.dialect.DB2Dialect
org.hibernate.dialect.DB2400Dialect
org.hibernate.dialect.DB2390Dialect
org.hibernate.dialect.PostgreSQLDialect
org.hibernate.dialect.MySQLDialect
org.hibernate.dialect.MySQLInnoDBDialect
org.hibernate.dialect.MySQLMyISAMDialect
org.hibernate.dialect.OracleDialect
org.hibernate.dialect.Oracle9Dialect
org.hibernate.dialect.SybaseDialect
org.hibernate.dialect.SybaseAnywhereDialect
org.hibernate.dialect.SQLServerDialect
org.hibernate.dialect.SAPDBDialect
org.hibernate.dialect.InformixDialect
org.hibernate.dialect.HSQLDialect
org.hibernate.dialect.IngresDialect
org.hibernate.dialect.ProgressDialect
●
●
●
●
●
MckoiSQL
Interbase
Pointbase
FrontBase
Firebird
org.hibernate.dialect.MckoiDialect
org.hibernate.dialect.InterbaseDialect
org.hibernate.dialect.PointbaseDialect
org.hibernate.dialect.FrontbaseDialect
org.hibernate.dialect.FirebirdDialect
Przykładowy plik – Hibernate.cfg.xml
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="connection.driver_class">org.hsqldb.jdbcDriver</property>
<property name="connection.url">jdbc:hsqldb:</property>
<property name="connection.username">user</property>
<property name="connection.password">tajne</property>
<property name="dialect">org.hibernate.dialect.HSQLDialect</property>
<property name="show_sql">true</property>
<property name="hbm2ddl.auto">create</property>
<mapping resource="Osoba.hbm.xml"/>
</session-factory>
</hibernate-configuration>
Połączenia z bazą danych możemy określać poprzez DriverManager:
<property name="connection.driver_class">org.hsqldb.jdbcDriver</property>
lub też poprzez DataSource podając źródło danych:
<property name="connection.datasource">jdbc:hsqldb/nazwa_bd</property>
Parametr <show_sql> jest parametrem, na który należy kłaść duży nacisk. Przydaje on się
wielokrotnie, gdy tworzy się oprogramowanie. Ustawienie właściwości hibernate.show_sql na
wartość true włącza wyświetlanie wszystkich utworzonych poleceń SQL na konsoli. Warto
korzystać z tej opcji w momencie poszukiwania błędów, poszukiwania wąskiego gardła lub po
prostu analizy działania Hibernate.
Z kolei parametr <hbm2ddl.auto> umożliwia tworzenie struktury bazy danych przy starcie aplikacji
(gdy hbm2ddl.auto wynosi create). Po przetestowaniu aplikacji parametr hbm2ddl.auto zaleca się
ustawić na validate, albo po prostu wyłączyć poprzez usunięcie, czy zakomentowanie linii
zawierającej parametr.
Do inicjalizacji Hibernate wystarczy teraz użycie następującego kodu:
SessionFactory sessions = new Configuration().configure().buildSessionFactory();
W momencie wywołania metody configure() Hibernate poszukuje pliku o nazwie hibernate.cfg.xml
w ścieżce wyszukiwania klas. Jeśli chce się dla pliku konfiguracyjnego zastosować inną nazwę
lub też plik znajduje się w niestandardowej lokalizacji, do metody configure() należy przekazać
ścieżkę .
SessionFactory sessions = new Configuration().configure("/hibernate-config/przyklad.cfg.xml")
.buildSessionFactory();
Jeśli ścieżka wyszukiwania klas zawiera pliki hibernate.properties i hibernate.cfg.xml, ustawienia
występujące w pliku XML nadpiszą ustawienia z pliku właściwości.
Rodzaje zapytań w Hibernate
●
Zapytania w języku HQL (Hibernate QueryLanguage):
– składnia podobna do SQL
– zorientowany obiektowo
– zapytania odwołują się do klas, a nie tabel
Podstawowym sposobem odczytu danych z bazy danych w Hibernate są zapytania formułowane w
języku HQL (Hibernate Query Language). Język ten pod względem składni bardzo przypomina
SQL. Podstawowa struktura zapytania to SELECT-FROM-WHERE-GROUP BY-HAVINGORDER BY, z tym że klauzula SELECT jest opcjonalna, gdy mają by odczytane całe obiekty. HQL
w odróżnieniu od SQL jest zorientowany obiektowo i operuje na klasach języka Java,a nie tabelach
relacyjnej bazy danych. HQL obsługuje dziedziczenie,polimorfizm i asocjacje między klasami.
1) 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]);
}
2) wykonywanie zapytań poprzez iterate() - zwraca wynik w kilku zapytaniach SELECT:
-Pobiera identyfikatory
-Oddzielne zapytania pobierające poszczególne instancje
-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());
}
3) Pobieranie obiektów z bazy danych:
Domyślnie w przypadku operacji połączenia, HQL nie pobiera natychmiast związanych obiektów i
kolekcji.Są one pobierane gdy nastąpi do nich pierwsze odwołanie (tryb „lazy fetching”).Dla wszystkich
typów zbiorów, poza tablicami, można włączyć późne ładowanie wykorzystując zapis lazy = "true".
Zastosowanie późnego ładowania umożliwia znaczne przyspieszenie szybkości działania aplikacji,
szczególnie takich operacji, jak wyszukiwanie. Domyślnie tryb lazy jest ustawiony dla relacji
@OneToMany i @ManyToMany. Pobranie encji następuje dopiero przy próbie odwołania się do
niej, np odczyt z kolekcji. Leniwe (opóźnione )pobieranie pól obiektów może być przydatne tylko
wtedy, gdy mamy w nich dużo danych i chcemy zaoszczędzić pracy zbieraczowi nieużytków
(garbage collection). Z kolei dla relacji @OnetoOne oraz @ManytoOne domyślnym rozwiązaniem
jest tzw. “eager fetching” czyli natychmiastowe ładowanie.HQL ignoruje ewentualne ustawienia podane
przy odwzorowaniu.Stanowi to problem, gdy odwołanie do dowiązanego obiektu lub kolekcji nastąpi po
zamknięciu sesji, w której wykonano zapytanie.Rozwiązaniem jest zastosowanie klauzul (działają dla
list()):
-
INNER JOIN FETCH - dla pobrania pojedynczych obiektów
LEFT JOIN FETCH - dla pobrania kolekcji
from Dept as dept left join fetch dept.emps as emp
from Emp as emp inner join fetch emp.dept
●
●
●
Zapytania w natywnym SQL, dające możliwość wykorzystania specyficznych dla
danego systemu konstrukcji składniowych np. CONNECT w Oracle.
Zapytania poprzez obiekty Criteria, umożliwiające budowę zapytań poprzez
obiektowe API
Zapytania poprzez obiekty Example, umożliwiające wyszukiwanie danych w oparciu
o przykadową instancję (mechanizm QBE – QueryByExample).
Reverse Engineering (podejście “z dołu do góry”)
W podejściu z dołu do góry tworzenie oprogramowania rozpoczyna się od istniejącego schematu
bazy danych i modelu danych. W tym przypadku najwygodniej jest najpierw skorzystać z narzędzia
Middlegen do wygenerowania plików odwzorowań Hibernate na podstawie istniejącej bazy danych,
a następnie uruchomić narzędzie hbm2java do wygenerowania szkieletu klas trwałych POJO.
Narzędzie hbm2java stosuje się do:
generowania kodu źródłowego klas POJO na podstawie plików odwzorowań Hibernate przy
podejściu od środka,
generowania kodu źródłowego klas POJO na podstawie plików odwzorowań Hibernate
uzyskanych automatycznie narzędziem Middlegen z istniejącego schematu bazy danych.
Narzędzie można dostosować do własnych potrzeb, korzystając na przykład z dodatkowych
metadanych (jak to miało miejsce w narzędziu hbm2ddl).
Najczęściej wygenerowane pliki i klasy trzeba poprawiać i rozszerzać ręcznie, ponieważ nie
wszystkie asocjacje klas i szczegóły metadanych można określić automatycznie na podstawie
schematu bazy danych.
Download