background image

HIBERNATE

Czyli relacyjne bazy danych

obiektowo

w Javie

Adam Buraczewski <aburacze@gmail.com>

Gliwice, 15 marca 2008 r.

background image

2

Plan wykładu

Wprowadzenie do idei Object-Relational Mapping

Dołączanie i konfigurowanie biblioteki Hibernate

Podstawowe powiązanie klas i tabel bazy danych

Tworzenie zapytań w językach SQL, HQL
oraz za pomocą Criteria Query

Bardziej zaawansowane struktury danych,
polimorfizm

Optymalizacja

Różne zaawansowane mechanizmy

background image

3

Przykładowa baza danych

Do celów prezentacji stworzono przykładową bazę 
danych genealogicznych

Tabele:

„city” (city_id, city_name) – miejscowości

„address” (address_id, city_id, street, house) – adresy

„person” (person_id, person_name, birth_date, gender, 

mother_id, father_id, address_id) – osoby z 

określeniem pokrewieństwa

„marriage” (husband_idwife_id, wedding_date) – 

informacje o małżeństwach

background image

4

Diagram E-R przykładowej bazy

PERSON

CITY

ADDRESS

*

1

1

*

background image

5

Diagram przykładowej bazy

PERSON

PERSON

(mother)

PERSON

(father)

MARRIAGE

1

1

1

1

*

*

0..1

0..1

background image

6

Cechy przykładowej bazy

Klucze sztuczne typu INTEGER, nadawane na 
podstawie sekwencji lub inaczej numerowane 
automatycznie – najwygodniejsze do stosowania z 
Hibernate (warto je stosować nawet gdy podczas 
normalizacji wyłania się klucz złożony)

Tabela „marriage” ma celowo wprowadzony klucz 
złożony w celu ilustracji jego obsługi w Hibernate

background image

7

Cechy typowej

relacyjnej bazy danych

Dane umieszczone w wielu znormalizowanych 
tabelach (3NF)

Poszczególne kolumny zawierają dane o 
wartościach „atomowych” (INTEGER, TEXT, DATE 
itp.)

Powiązania za pomocą kluczy

Dodatkowe więzy integralności, triggery

background image

8

Co dają relacyjne bazy danych

Świetnie opracowana teoria i metody 
projektowania

Dużo gotowych „silników”, od systemów 
wbudowanych do dużych klastrów serwerów

Znormalizowany, bogaty język SQL

Wydajność, indeksy, wieloużytkownikowość

Więzy integralności, triggery, transakcje

Procedury składowane

Gotowe narzędzia do administracji, wykonywania 
kopii bezpieczeństwa itp.

background image

9

Co dają transakcje

Atomicity – efekt transakcji jest zapisywany w bazie 
danych w całości, albo cała transakcja jest 
wycofywana

Consistency – po zakończeniu transakcji baza 
pozostaje zawsze spójna

Isolation – efekt transakcji jest widziany przez 
innych użytkowników dopiero po jej zatwierdzeniu

Durability – baza danych gwarantuje że efektu 
zatwierdzonej transakcji nie da się już 
podważyć/wycofać

background image

10

Dostęp do bazy przez JDBC

Rozwiązanie najprostsze, działa z podstawową 
wersją Java Standard Edition

Wyganania: kompilator Javy, oraz „sterownik”, np. 
postgresql-8.3-603.jdbc3.jar

Uruchamianie programu:

java -Djdbc.drivers=org.postgresql.Driver
   -classpath postgresql-8.3-603.jdbc3.jar
   ...

background image

11

Dostęp do bazy przez JDBC

(nawiązanie połączenia)

import java.sql.*;
import java.util.Properties;

Properties p = new Properties();
p.setProperty(„user”, „użytkownik”);
p.setProperty(„password”, „hasło”);
try {
Connection dbConn = DriverManager.getConnection(
  „jdbc:postgresql://komputer/baza”, p)
catch (SQLException e) {
  // Połączenie nie udało się...
}

background image

12

Dostęp do bazy przez JDBC

(proste zapytania)

try {
  dbConn.begin();
  Statement s = dbConn.createStatement();
  ResultSet r = s.executeQuery(
    „SELECT person_name FROM person ”);
  while(r.next()) {
    System.out.println(
      r.getString(„person_name”));
  }
  dbConn.commit();
catch (SQLException e) {
  dbConn.rollback();
}
dbConn.close();

background image

13

Klasy Statement i ResultSet

Statement – zapytanie SQL (wymaga SQL w 
dialekcie odpowiedniej bazy)

executeQuery(„SELECT ...”) – zapytanie zwracające 

wynik

executeUpdate(„INSERT INTO ...”) – polecenie DML

ResultSet – wynik zapytania SELECT

next() - przechodzi do następnego wiersza, zwraca true, 

gdy OK

getXXX(int), getXXX(String) – zwraca wynik z 

bieżącego wiersza, z kolumny o danym numerze lub 

nazwie (XXX = Int, Double, String, Date, Time itp.)

background image

14

Prepared Statements

Pozwalają na przygotowanie polecenia SQL i 
wielokrotne uruchamianie go dla różnych 
parametrów (szybsze gdy obsługiwane specjalnie 
przez serwer bazodanowy, np. PostgreSQL)

Funkcje setXXX(nr, wartość) – ustawianie wartości 
parametrów

Uniknięcie ataków typu „SQL Injection” - sterownik 
dba o odpowiednie „escape'owanie” i konwersję 
parametrów

background image

15

Prepared Statements

PreparedStatement s = dbConn.prepareStatement(
  „SELECT * FROM person WHERE person_name = ?”);

s.setString(1, „Jan”);
ResultSet r = s.executeQuery();

s.setString(1, „Elżbieta”);
r = s.executeQuery();

// Poniższe polecenie zadziała bezpiecznie
s.setString(1,
  „'; DELETE FROM person; SELECT '”);
r = s.executeQuery();

background image

16

Savepoints

Służą do realizacji eleganckiej obsługi błędów

W przypadku problemu wycofanie transakcji 
następuje do określonego punktu, nie trzeba 
wycofywać całej transakcji

Savepoint sp = dbConn.setSavepoint();
try {
  // Operacje na bazie danych
catch (Exception e) { 
  dbConn.rollback(sp); throw e;
finally {
  dbConn.releaseSavepoint(sp);
}

background image

17

Plain Old Java Objects

class Person {
  private int id;
  private String personName;
  private Date birthDate;
  private char gender;

  public int getId() { return id; }
  public void setId(int id) { this.id = id; }

  public String getPersonName() {
    return personName; }
  public void setPersonName(String personName) {
    this.personName = personName; }
}

background image

18

Plain Old Java Objects

Często wykorzystywany model budowy klasy, 
zgodny ze standardem Java Beans (nadaje się do 
automatycznej obsługi za pomocą pakietu 
java.beans)

Poszczególne zmienne (id, personName itp.) 
prywatne, dostępne jedynie dla funkcji klasy

Dostęp do zmennych za pomocą pary getter/setter 
(getNazwaZmiennej/setNazwaZmiennej)

Dla zmiennych typu boolean można stosować 
isNazwaZmiennej zamiast getNazwaZmiennej

background image

19

Łączenie JDBC i obiektowości

Konieczność przepisania danych z ResultSet do 
obiektu

ResultSet r = s.executeQuery();
r.next();
Person p = new Person();
p.setId(r.getInteger(„person_id”));
p.setPersonName(r.getString(„person_name”));
p.setBirthDate(r.getDate(„birth_date”));
p.setGenre(r.getChar(„genre”));
//...

background image

20

Łączenie JDBC i obiektowości

Modyfikacja obiektu – konieczność sprawdzenia, 
czy obiekt się zmienił i wykonania odpowiedniego 
polecenia UPDATE (np. dodatkowa flaga 
informująca czy w obiekcie nastąpiły zmiany)

Nowy obiekt, usunięcie obiektu – konieczność 
wygenerowania odpowiednich poleceń INSERT lub 
UPDATE

Problem ustalenia kolejności zapisywania danych 
do bazy, gdy kilka obiektów zależy od siebie 
nawzajem

background image

21

JDBC – podsumowanie

Mechanizm szybki i prosty w użyciu

Dużo ciekawych możlwiości (savepoints, prepared 
statements itp.)

Problemy gdy wyniki zapytań mają być przepisane 
do obiektu, oraz zmiany w obiekcie mają być 
naniesione w bazie danych itp.

Problemy gdy w bazie danych ma być zapisanych 
wiele powiązanych ze sobą obiektów 

background image

22

Object-Relational Mapping

Automatyczna wypełnienie obiektów Java na 
podstawie wyniku zapytania SQL

Przetworzenie zmian w obiekcie do odpowiednich 
poleceń INSERT/UPDATE/DELETE

Rozwiązywanie problemów związanych z zapisem 
i odczytem powiązanych obiektów

background image

23

Hibernate

ORM

Możliwość zapisu zapytań w języku HQL 
(Hibernate Query Language) i Criteria Query – 
automatyczna translacja na odpowiedni dialekt 
SQL

Cache dla obiektów i zapytań

Kontrola transakcji i reakcja na błędy

Dużo dodatkowych możliwości i narzędzi

background image

24

Hibernate – podstawowe klasy

SessionFactory – zarządza konfiguracją, tworzy 
sesje

openSession() - tworzy nową sesję

Session – opisuje połączenie z bazą danych

getTransaction() - tworzy obiekt transakcji

Transaction – opisuje transakcję

begin()

commit()

rollback()

background image

25

Konfiguracja

Plik hibernate.cfg.xml:

<?xml version="1.0" encoding="ISO-8859-2"?>
<hibernate-configuration>
  <session-factory>
    <property name="connection.driver_class">
      org.postgresql.Driver</property>
    <property name="connection.url">
      jdbc:postgresql:genealogy</property>
    <property name="connection.username">
      użytkownik</property>
    <property name="connection.password">
      hasło</property>

background image

26

Konfiguracja

    <property name="dialect">
      org.hibernate.dialect.PostgreSQLDialect

   </property>
   <property name="show_sql">false</property>
 
   <mapping resource=”ścieżka/do/mapy1.xml”/> 
   <mapping resource=”ścieżka/do/mapy2.xml”/> 
   <mapping resource=”ścieżka/do/mapy3.xml”/> 

 </session-factory>
</hibernate-configuration>

background image

27

Mapowanie bazy danych

Najprościej: plik XML (zwykle „klasa.hbm.xml”):

<?xml version="1.0" encoding="ISO-8859-2"?>
<hibernate-mapping>

  <class name=”Person” table=”person”>

    <id name=”id” type=”integer”
        column=”person_id” unsaved-value=”0”>
      <generator class=”sequence”>
        <param name=”sequence”>
          person_seq</param>
      </generator>
    </id>

background image

28

Mapowanie bazy danych

    <property name="personName"
      type=”string” column=”person_name”
      not-null=”true” unique=”false”/>
    <property name="birthDate"
      type=”date” column=”birth_date”
      not-null=”true” unique=”false”/>
 </class>
</hibernate-mapping>

background image

29

Zwyczajowe rozmieszczenie plików

  |
  |-- hibernate.cfg.xml
  |-- pakiet1
  |     |-- Klasa1.java
  |     |-- Klasa1.hbm.xml
  |     |-- Klasa2.java
  |     |-- Klasa2.hbm.xml
  |-- pakiet2

background image

30

Podstawowy program

SessionFactory sf
  = new Configuration().configure()
    .buildSessionFactory();
Session s = sf.getCurrentSession();
Transaction t = s.getTransaction();
t.beginTransaction();

// Operacje na obiektach

t.commit();

background image

31

Podstawowe operacje na obiektach

Person p1 = (Person) s.getObject(
  Person.class, 1);

s.lock(p1, LockMode.UPDATE);
p1.setPersonName(„Antoni”);

Person p2 = new Person();
p2.setPersonName(„Zdzisław”);
s.save(p2);

s.flush();

s.evict(p1);

background image

32

Operacje na obiektach

Klasa Session:

get(klasa, identyfikator) – zwraca obiekt lub null

save(obiekt) – zapisuje obiekt w bazie

delete(obiekt) – usuwa obiekt z bazy

lock(obiekt, tryb) – blokuje obiekt w bazie

Wykonanie save() powoduje nadanie obiektowi 
identyfikatora bazodanowego (np. na podstawie 
sekwencji)

background image

33

Zapytania HQL

Język HQL pozwala na uniezależnienie się od 
dialektu SQL:

Query q = s.createQuery(
  „from Person where personName = :name”)
q.setString(„name”, „Antoni”);
q.setCacheable(true);

for (Person p : (List<Person>) q.list()) {
  System.out.println(p.getBirthdayDate());
}

background image

34

Klasa Query

Funkcje setXXX(), podobnie jak 
PreparedStatement z JDBC

Można stosować :nazwa do oznaczania 
parametrów zapytań

Funkcje:

list() - zwraca wynik w postaci listy obiektów

iterate() - zwraca iterator po wynikach (oszczędza 

pamięć)

uniqueResult() - gdy wiemy, że wynik będzie dokładnie 

jeden

background image

35

Składnia HQL

from pakiet.klasa – nie ma wyliczenia kolumn 
(SELECT), bo i tak wszystkie są odczytywane, 
chyba że ograniczamy listę kolumn

Typowe operatory języka SQL (AND, OR, NOT, =, 
<, >, LIKE itp.)

Możliwość stosowania złączeń (inner/outer join)

Możliwość agregacji i grupowania

Elementy języka Java (stałe, klasy)

Dodatkowe funkcje: empty(), size(), maxelement()

background image

36

Ciąg dalszy nastąpi...

Uzupełnieniem prezentacji jest kod programów, 
które były pokazywane na żywo podczas wykładu

Temat jest dopiero napoczęty – ciąg dalszy bez 
wątpienia nastąpi