Tworzenie warstwy
trwałości danych w
oparciu o Hibernate
Rafał Kasprzyk
Rafał Kasprzyk
Ograniczenia na POJO
Wszystkie pola trwałe muszą być prywatne
Należy stworzyć accessory i mutatory dla
pól trwałych
Musi istnieć bezargumentowy konstruktor
może być domyślny
Jedno z pól powinno pełnić rolę
identyfikatora encji (opcjonalnie)
Hibernate może sam zarządzać
identyfikatorami obiektów, jednak nie jest to
zalecane
Zalecany jest sztuczny identyfikator, typu nie-
prostego
Możliwość przypisania null
Zaleca się stosowanie wspólnego nazewnictwa
np. id
Modyfikator final ogranicza możliwości
strojenia wydajności
Rafał Kasprzyk
Zapytania do bazy danych
Zapytania w HQL (ang. Hibernate Query
Language)
Język syntaktycznie i semantycznie podobny do SQL
SELECT, FROM, WHERE, GROUP BY, HAVING, ORDER BY,
złączenia, podzapytania (o ile wspiera je DBMS)
Zorientowany obiektowo
Dziedziczenie, asocjacje, …
Zapytania poprzez obiekty Criteria
Zapytania budowane poprzez obiektowe API
Zapytania poprzez obiekty Example
QBE (ang. Query By Example)
Zapytanie budowane w oparciu o przykładową instancję
Filtry
Wykorzystywane do kolekcji i/lub tablic
Zapytania w zwykłym SQL
Możliwość wykorzystania specyficznych konstrukcji np.
CONNECT
Rafał Kasprzyk
Zapytania HQL
Domyślny mechanizm zapytań
HQL w swej strukturze jest bardzo
podobny do SQL
Zapytania HQL definiowane są w dwóch
miejscach:
W pliku definicji mapowania (preferowany
sposób)
Możliwość modyfikacji zapytania bez rekompilacji
kodu
Bezpośrednio w kodzie źródłowym
Obiekt reprezentujący zapytanie
uzyskuje się z sesji Hibernate
Umożliwia on przypisanie parametrom
zapytania wartości oraz pobranie wyniku w
postaci listy bądź iteratora
Rafał Kasprzyk
HQL (wykorzystanie listy)
Wynik zwracany jest do kolekcji w
pamięci
Jedno odwołanie do bazy danych
List users = (List)session.createQuery(
"from User as user where user.surname = 'Kowalski'")
.list();
for (int i=0; i<users.size(); i++)
System.out.println(((User)users.get(i)).getName());
List users = (List)session.createQuery(
"select user.id, user.surname from User as user where
user.name = 'Jan'")
.list();
for (int i=0; i<users.size(); i++) {
Object [] tab = (Object []) users.get(i);
System.out.println(tab[0]+" "+tab[1]);
}
Rafał Kasprzyk
HQL (wykorzystanie iteratora)
Wynik nie jest natychmiast ładowany
do pamięci
Pobranie identyfikatorów
Wielokrotne wykonanie zapytania
SELECT
Każda instancja wymaga odwołania się do
bazy danych lub pamięci podręcznej jeżeli
obiekty są już załadowane
Iterator users = session.createQuery(
"from User as user where user.surname = 'Kowalski'")
.iterate();
while (users.hasNext()) {
User user = (User) users.next();
System.out.println(user.getName());
}
Rafał Kasprzyk
Przykłady zapytań w HQL
Rafał Kasprzyk
HQL – operacje złączenia
Domyślnie w przypadku złączenia,
Hibernate nie pobiera natychmiast
związanych obiektów i kolekcji
Obiekty te są pobierane, gdy wystąpi pierwsze
do nich odwołanie (tryb lazy)
Ewentualne ustawienia podane w pliku
odwzorowań są ignorowane
Prowadzi to do problemów jeśli odwołanie do
dowiązanego obiektu lub kolekcji nastąpi po
zamknięciu sesji, w której wykonano zapytanie
Rozwiązaniem są klauzule (wynik zapisać do
listy!!!)
INNER JOIN FETCH – dla pobrania pojedynczych
obiektów
LEFT JOIN FETCH – dla pobrania kolekcji
from Employee as emp inner join fetch emp.dept
from Department as dept left join fetch dept.emps as emp
Rafał Kasprzyk
HQL – dodatkowe mechanizmy
Zapytania sparametryzowane
Operator ?
List depts = (List)s.createQuery(
"from Department as dept
where dept.loc = ?")
.setString(0, "Warszawa");
// numeracja od zera!
.list();
Parametry nazwane :name
List depts = (List)s.createQuery(
"from Department as dept
where dept.loc = :city")
.setString("city", "Warszawa");
.list();
Rafał Kasprzyk
HQL – dodatkowe mechanizmy
Zawężanie wyniku zapytania (ang. pagination)
Query q = s.createQuery(
"from Deptartment as dept
where dept.loc = 'Opole'");
q.setFirstResult(0);
q.setMaxResults(10);
List depts = q.list();
Kursory - przewijane wyniki zapytań
Wymaga otwartego połączenia z bazą i otwartego
kursora
Sterownik JDBC musi wspierać kursory
Query q = s.createQuery(
"select ... from ...");
ScrollableResults kursor = q.scroll();
Rafał Kasprzyk
HQL – zapytania nazwane
<query
name="Employee.by.name.and.minimum.age">
<![CDATA[from Employee as emp
where emp.name = ? and emp.age > ?] ]>
</query>
Query query = session.getNamedQuery(
"Employee.by.name.and.minimum.age");
query.setString(0, name);
query.setInt(1, minAge);
List emps = query.list();
Rafał Kasprzyk
delete i update z poziomu HQL
Możliwość wykonania poleceń delete
i update z poziomu HQL wprowadzona
dopiero Hibernate w wersji 3
Przez długi okres twórcy Hibernate
wzbraniali się przed implementacją takiej
możliwości z powodu zawiłości jakie
może to wprowadzić w kontekście cache
pierwszego i drugiego poziomu
public int clearEmployees() {
session session = factory.getSession();
return session.createQuery(„delete
from Employee”).executeUpdate();
}
Rafał Kasprzyk
Criteria API – zapytania
dynamiczne
Mechanizm pozwalający na dynamiczne
konstruowanie zapytań
Sprawdza się wszędzie tam, gdzie trudno do
końca stwierdzić, czego użytkownik końcowy
aplikacji będzie chciał pozyskać z bazy
danych
Wszelkie możliwe raporty, jakie będą generowali
użytkownicy mogą nie być do końca znane
Można wówczas dostarczyć graficznego narzędzia
do projektowania raportów wykorzystując Criteria
API do realizacji właśnie dynamicznych zapytań
Obiekt reprezentujący zapytanie uzyskuje się
z sesji Hibernate
Za pomocą metody add() wprowadza się do
zapytania ograniczenia, a wynik może przyjąć
postać listy bądź iteratora
Criteria API pozwala na prostą nawigację po
zapytaniach i budowanie podzapytań
Rafał Kasprzyk
Korzystanie z obiektu Criteria
List emps = session
.createCriteria(Employee.class)
.add(Restrictions.like("surname", "K%"))
.add(Restrictions.eq("sex", 'M'))
.add(Restrictions.between("salary", min, max)
.list();
List poorEmps = session
.createCriteria(Employee.class)
.add(Restrictions.like("name", "A%"))
.add(Restrictions.or
(Restrictions.eq("salary",new Integer(0)),
Restrictions.isNull("age")))
.list();
Rafał Kasprzyk
Korzystanie z obiektu Criteria
List emps = session
.createCriteria(Employee.class)
.add( Restrictions.like("surname", "K%"))
.addOrder(Order.asc(surname))
.addOrder(Order.desc(name))
.setMaxResult(10);
.setFirstResult(50);
.list();
Rafał Kasprzyk
Korzystanie z obiektu Criteria
List emps = session
.createCriteria(Employee.class)
.add( Restrictions.in("name", new String[] {
"Piotr","Anna","Katarzyna" } ))
.add( Restrictions.disjunction()
.add( Restrictions.isNull("salary"))
.add( Restrictions.eq("salary", new Integer(0)))
)
.list();
List emps = session.createCriteria(Employee.class)
.add( Restrictions.sql(
"lower({alias}.name) like lower(?)",
"K%", Hibernate.STRING))
.list();
Rafał Kasprzyk
Korzystanie z obiektu Criteria
Object object = session
.createCriteria(Employee.class)
.setProjection(
Projections.projectionList()
.add(Projections.rowCount())
.add(Projections.min("salary"))
.add(Projections.max("salary"))
.add(Projections.avg("salary"))
)
.uniqueResult();
Rafał Kasprzyk
Korzystanie z obiektu Criteria
List depts = session
.createCriteria(Department.class)
.add(Restrictions.eq("loc", "Opole"))
.createCriteria("emps")
.add(Restrictions.like("name", "K%"))
.list();
Rafał Kasprzyk
Przykład wykorzystania
Criteria API
Criteria criteria = session
.createCriteria(Employee.class);
criteria.add(Expression.like("name", name));
criteria.addOrder(Order.asc("name"));
criteria.setCacheable(true);
List emps = criteria.list();
Rafał Kasprzyk
Wykorzystanie obiektów Example
Zapytanie budowane są w oparciu o
przykładową instancję klasy trwałej
Employee emp = new Employee();
emp.setSex('F');
List emps = session
.createCriteria(Employee.class)
.add(Example.create(emp)).list();
List coworkers = session
.createCriteria(Employee.class)
.add( Example.create(emp))
.createCriteria("unit")
.add( Example.create( emp.getUnit() ) )
.list();
Rafał Kasprzyk
Zapytania natywne SQL
W szczególnych przypadkach, gdy
chodzi o optymalizację zapytań
konieczne okazuje się wykorzystanie
natywnych zapytań SQL
Programista doskonale znający
wykorzystywany silnik bazy danych
jest w stanie stworzyć zapytanie,
które jest wydajniejsze niż jego
odpowiednik wygenerowany z
wykorzystaniem HQL lub Criteria API
Powstały kod staje się jednak trudno
przenośny
Rafał Kasprzyk
Przykłady zapytań natywnych
SQL
List emps = session.createSQLQuery(
"select * from emps")
.addEntity(Employee.class)
.list();
List emps = session.createSQLQuery(
"select {emp.*} from emps emp")
.addEntity("emp", Employee.class)
.list();
Query sqlQuery = session.createSQLQuery(
"select {emp.*} from emps {emp}",
"emp", Employee.class);
sqlQuery.setMaxResults(20);
List emps = sqlQuery.list();
Rafał Kasprzyk
Przykład wykorzystania Filtrów
Collection Emps = session
.createFilter(dept.getEmps(),
"where this.salary > 10 000" )
.list();
Collection tenEmps = session
.createFilter(dept.getEmps(), "")
.setFirstResult(0).setMaxResults(10)
.list();
Rafał Kasprzyk
Podsumowanie
Ograniczenia na POJO
Zapytania HQL
Criteria API
Zapytania poprzez obiekt Example
Filtry
Zapytania natywne SQL