Programowanie wielowarstwowe i komponentowe
HIBERNATE CD.
Rodzaje relacji Jeden do wielu Pojedyncza Osoba ma wiele Wpisów Wiele do jednego Wiele Wpisów należy do jednej Osoby Jeden do jednego Pojedyncza Osoba ma jeden Adres zameldowania Pojedynczy Adres zameldowania przynależy do jednej Osoby Wiele do wielu Wiele Studentów ma wiele Przedmiotów Wiele Przedmiotów przynależy do wielu Studentów
Tworzenie relacji 1:M/M:1 Po stronie pojedynczego obiektu mapowana jest kolekcja obiektów (List, Set, Map itp.) <one-to-many class="pl.example.wpis" /> Po stronie wielu obiektów, mapowany jest pojedynczy obiekt M:M 1:1 <many-to-one name="osoba" column="osoba_id class="pl.example.osoba" > Po obu stronach jest mapowana kolekcja <many-to-many column="przedmiot_id class="pl.example.przedmiot" /> <many-to-many column="student_id class="pl.example.student" /> Po obu stronach mapowane są obiekty <one-to-one name="osoba" class="pl.example.osoba /> <one-to-one name="adres class="pl.example.adres />
Typy kolekcji Hibernate <set> Wymaga podania kolumny wartości <map> Wymaga określenia kolumn klucza i wartości <list> Posortowawna, wymaga zaindeksowanej kolumny w tabeli pojedynczego obiektu <array> Mapowanie do zwykłej tablicy, wymaga zaindeksowanej kolumny w tabeli pojedynczego obiektu <bag> Podobnie jak lista, ale nie wymaga indeksu <idbag> Używany do relacji wiele do wielu
Połączenie poprzez <set> Powiązanie Osoba Wpis (do bloga) Mapowanie Osoba w klasie Wpis: <many-to-one name="osoba" column="osoba_id class="pl.example.osoba" /> Mapowanie zbioru Wpisów w klasie Osoba: <set name="wpisy" inverse="true > <key column="osoba_id not-null="true /> <one-to-many class="pl.example.wpis" /> </set>
Przykład relacji 1:M Relacja Osoba-Wpisy (np. do bloga) public class Osoba { private int id; private String imie; private String nazwisko; private Set<Wpis> wpisy; public class Wpis { private int id; private Osoba osoba; private String text;
Przykład relacji 1:M cd. Wpis.hbm.cfg <?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> <class name="pl.example.wpis" table="wpis"> <id name="id" type="int"> <column name="id" /> <generator class="identity" /> </id> <many-to-one name="osoba" class="pl.example.osoba" > <column name="osoba_id" /> </many-to-one> <property name="text" type="java.lang.string"> <column name="text" /> </property> </class> </hibernate-mapping>
Przykład relacji 1:M cd. Osoba.hbm.cfg <?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> <class name="pl.example.osoba" table="osoba"> <id name="id" type="int"> <column name="id" /> <generator class="identity" /> </id> <property name="imie" type="java.lang.string"> <column name="imie" /> </property> <property name="nazwisko" type="java.lang.string"> <column name="nazwisko" /> </property> <set name="wpisy" table="wpis" inverse="true" lazy="true"> <key> <column name="osoba_id" /> </key> <one-to-many class="pl.example.wpis" /> </set> </class> </hibernate-mapping>
Przykład relacji 1:M cd. 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="hibernate.connection.driver_class">com.mysql.jdbc.driver</property> <property name="hibernate.connection.password">passsword</property> <property name="hibernate.connection.url">jdbc:mysql://localhost/test7</property> <property name="hibernate.connection.username">root</property> <property name="hibernate.dialect">org.hibernate.dialect.mysql5dialect</property> <property name="show_sql">true</property> <property name="hbm2ddl.auto">update</property> </session-factory> </hibernate-configuration>
Przykład relacji 1:M cd. public class HibernateUtil { private static final SessionFactory sessionfactory; private static ServiceRegistry serviceregistry; static { try { Configuration config = new Configuration().configure(); config.addclass(pl.example.osoba.class); config.addclass(pl.example.wpis.class); } serviceregistry = new StandardServiceRegistryBuilder().applySettings(config.getProperties()).build(); sessionfactory = config.buildsessionfactory(serviceregistry); } catch (Throwable ex) { System.err.println("Initial SessionFactory creation failed." + ex); throw new ExceptionInInitializerError(ex); } public static SessionFactory getsessionfactory() { return sessionfactory; } }
Przykład relacji 1:M cd. SessionFactory sf = HibernateUtil.getSessionFactory(); Session session = sf.opensession(); Transaction t = session.begintransaction(); Osoba o = new Osoba(); o.setimie("jan"); session.save(o); Wpis w = new Wpis(); w.settext("hello world!"); w.setosoba(o); session.save(w); w = new Wpis(); w.settext("inny tekst "); w.setosoba(o); session.save(w); session.flush(); t.commit(); session.close();
Zapytania do bazy danych Zapytania w języku HQL (Hibernate Query Language) Język od strony składni wyglądający jak SQL SELECT, FROM, WHERE, GROUP BY, HAVING, ORDER BY, połączenia, podzapytania (jeśli wykorzystywany SZBD je wspiera) W pełni zorientowany obiektowo dziedziczenie, polimorfizm, asocjacje Zapytania w natywnym SQL Możliwość wykorzystania specyficznych konstrukcji np. CONNECT Zapytania poprzez obiekty Criteria Budowa zapytań poprzez obiektowe API Zapytania poprzez obiekty Example Kryteria zapytania budowane w oparciu o przykładową instancję (QBE Query By Example) Filtry Aplikowane do kolekcji lub tablic
HQL list() Wykonywanie zapytań poprzez list() Zwraca cały wynik zapytania do kolekcji w pamięci (instancje pozostają w stanie trwałym) Przykład List<Wpis> wpisy = (List<Wpis>)session.createQuery( "from Wpis w where osoba.id=2").list(); for (Wpis wpis : wpisy) { System.out.println( wpis.getosoba().getimie() +" napisal " + wpis.gettext()); } List<Osoba> osoby = (List<Osoba>)session.createQuery( "from Osoba where id=11").list(); Set<Wpis> wpisyosoby = osoby.get(0).getwpisy(); for (Wpis wpis : wpisyosoby) { System.out.println( wpis.gettext() ); }
HQL iterate() Wykonywanie zapytań poprzez iterate() Zwraca wynik w kilku zapytaniach SELECT: Pobranie identyfikatorów Oddzielne zapytania pobierające poszczególne instancje Może być efektywniejsze od list(), gdy instancje już są w pamięci podręcznej, ale zazwyczaj wolniejsze Iterator osoby = session.createquery( "from osoba as os where os.miejscowosc = 'Częstochowa'").iterate(); while (osoby.hasnext()) { Osoba o = (Osoba) osoby.next(); }
Przykłady zapytań HQL from pl.example.osoba from Osoba from java.lang.object from Osoba where miejscowosc is not null order by naziwisko, imie from Osoba o, Wpis w where o.id = w.osoba.id select os.nazwisko from Osoba os where os.naziwsko like K% select sum(zarobki) from Osoba group by stanowisko select sum(zarobki) from Osoba group by stanowisko having sum(zarobki) > 2000
HQL Pobieranie obiektów z bazy danych Domyślnie w przypadku operacji połączenia, HQL nie pobiera natychmiast związanych obiektów i kolekcji Domyślnie są one pobierane gdy nastąpi do nich pierwsze odwołanie (tryb lazy ) HQL ignoruje w tym względzie 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 Osoba as os left join fetch os.wpisy as w from Wpis as w inner join fetch w.osoba
Przetwarzanie wyników zapytań Zapytania sparametryzowane (styl? lub :nazwa) List depts = (List)session.createQuery( "from Osoba as os where os.miejscowosc =?" ).setstring(0, "Częstochowa").list(); Paginacja Query q = session.createquery("from Osoba as os where os.loc = 'Częstochowa'"); q.setfirstresult(0); q.setmaxresults(2); List depts = q.list(); Przewijalne wyniki zapytań Poprzez rzutowanie do ScrollableResults Wymaga otwartego połączenia z bazą i otwartego kursora Dostępne gdy sterownik JDBC wspiera przewijalne zbiory wynikowe Query q = session.createquery("select... from..."); ScrollableResults kursor = q.scroll();
Zapytania w Hibernate Różne możliwości wykonania zapytania w Hibernate Zapytanie przez ID Zapytanie przez kryteria Zapytanie przez przykład Hibernate Query Language (HQL) Zapytania klasycznym SQL przez Hibernate Hibernate wykonuje zoptymalizowane zapytania w zależności od rodzaju bazy danych
Zapytanie przez ID Wyszukuje obiekty po identyfikatorze Szybkie typ zapytania, ale uzyskiwany jest jedynie pojedynczy obiekt Osoba osoba = (Osoba)session.get( Osoba.class, osobaid);
Zapytanie przez kryteria Zapytanie jest tworzone przez wiele kryteriów Wykorzystywane są 4 interfejsy: org.hibernate.criteria Bazowy obiekt dla tego typu wyszukiwania, tworzony przez session Zawiera wszystkie restrykcje, agregacje, sortowania dla pojedynczego zapytania org.hibernate.detachedcriteria Podobnie jak Criteria, ale dołączany do sesji i uruchamiany org.hibernate.criterion.criterion Reprezentuje pojedyncze ograniczenie dla danego zapytania Odpowiednik where w zapytaniu SQL org.hibernate.criterion.restrictions Klasa pomocnicza używana do tworzenia obiektów klasy Criterion
org.hibernate.criteria Tworzenie poprzez podanie głównej klasy session.createcriteria(class); session.createcriteria(string classname); Dodanie restrykcji (klauzula where) addcriterion(criterion criterion); Dodanie asocjacji, dodanie asocjacji jako aliasu createalias(string assocjacjonpath, String alias); Dodanie sortownia (order by) addorder(order order) Rezultaty jako lista list(); Rezultat jako pojedynczy obiekt uniqueresult();
org.hibernate.detachedcriteria Nie jest wymagana sesja, aby utworzyć obiekt DetachedCriteria.forClass(Class); DetachedCriteria.forClass(String classname); Tworzenie przez statyczne metody Metody identyczne jak w Criteria Za wyjątkiem metod otrzymujących wyniki list() i uniqueresult() Do otrzymania wyników niezbędne jest utworzenie obiektu Criteria getexecutablecriteria(session);
org.hibernate.criterion.restrictions lt(string propertyname, String value); gt(string propertyname, String value); eq(string properyname, String value); ne(string propertyname, String value); like(string propertyname, String value); isempty(string propertyname, String value); isnotempty(string propertyname, String value); isnull(string propertyname, String value); isnotnull(string propertyname, String value); in(string propertyname, Collection values); alleq(map propertynamevalues); between(string propertyname, Object lo, Object hi);
Budowanie zapytania Criteria criteria = session.createcriteria(osoba.class); List osoby = criteria.list(); Criteria criteria = session.createcriteria(osoba.class).addorder(order.asc("nazwisko")); List osobyposortowaneponazwisku = criteria.list();
Wyszukiwanie pojedynczych obiektów Criteria criteria = session.createcriteria(osoba.class); Criterion restrictbyemail = Restrictions.eq("email", "jan@gmail.com"); cirteria.add(restrictbyemail); Osoba osoba = (Osoba)criteria.uniqueResult(); Inaczej: Osoba osoba = (Osoba)session.createCriteria(Osoba.class).add(Restriction. eq("email", "jan@gmail.com") ).uniqueresult();
Inne przykłady List osoby = session.createcriteria(osoba.class).add( Restrictions.between("zarobki",1000, 5000) ).list(); List osoby = session.createcriteria(osoba.class).add( Restrictions.like("nazwisko","K%").ignoreCase() ).add( Restrictions.like("imie","J%").ignoreCase() ).list();
OR i AND List osoby = session.createcriteria(osoba.class).add(restrictions.or( Restrictions.and( Restrictions.like("nazwisko","K%"), Restrictions.like("imie","J%") ), Restrictions.like("email", "%gmail.com") ) ).list()
Użycie DetachedCriteria DetachedCriteria detachedcriteria = DetachedCriteria.forClass(Osoba.class).add(Restrictions.ilike("nazwisko","K%")); Session session = HibernateUtil.getSessionFactory().getCurrentSession(); Criteria criteria = detachedcriteria.getexecutablecriteria(session); List accountowners = regularcriteria.list(); Lub: List accountowners = detachedcriteria.getexecutablecriteria(session).list();
Zapytanie przez przykład Zapytanie przez przykład polega na przygotowaniu przykładowego obiektu i na jego podstawie wygenerowania zapytania do bazy danych Klasa org.hibernate.criterion.example Obiekt tworzony przez create(object obj)
Zapytanie przez przykład Osoba osoba = new Osoba(); osoba.setnazwisko("kowalski"); Example osobaprzyklad = Example.create(osoba); List osoby = session.createcriteria(osoba.class).add(osobaprzyklad).list();
Ustawienie obiektu-przykładu Domyślnie ignorowane pola: Identyfikator Asocjacje Wartości null Dodatkowe ustawienia: enablelike(matchmode mode) Włączenie like do wszystkich String-ów ignorecase() excludezeroes(); Wyłącznie zer excludeproperty(string name); Wyłączenie konkretnej właściwości excludenone(); Nie wyłączaj zer i null-i
Dziedziczenie Dziedziczenie w Javie: extends lub implements Problem z dziedziczeniem w relacyjnej bazie danych Hibernate cztery sposoby na dziedziczenie Bez dziedziczenia Tabela dla konkretnej klasy Tabela dla hierarchii klas Tabela dla subklasy
Bez dziedziczenia Jedna tabela jest przeznaczona dla konkretnej klasy Mapowanie zawiera wszystkie pola, nawet wspólne dla innych klas Oddzielne pliki mapowania dla każdej z klas
Bez dziedziczenia KontoROR.java public class KontoROR extends Konto { private int kontororid; private String dostep; private double saldo; private Date datautworzenia; KontoOszczednosciowe.java public class KontoOszczednosciowe extends Konto { private int kontooszczednoscioweid; private double procent; private double saldo; private Date datautworzenia;
Bez dziedziczenia <hibernate-mapping> <class name="pl.example.kontooszczednosciowe" table="kontooszczednosciowe"> <id name="kontooszczednoscioweid" type="int"> <column name="kontooszczednoscioweid" /> <generator class="identity" /> </id> <property name="saldo" type="double"> <column name="saldo" /> </property> <property name="datautworzenia" type="timestamp"> <column name="data_utworzenia" /> </property> <property name="procent" type="double"> <column name="procent" /> </property> </class> </hibernate-mapping>
Bez dziedziczenia <hibernate-mapping> <class name="pl.example.kontoror" table="kontoror"> <id name="kontororid" type="int"> <column name="kontororid" /> <generator class="identity" /> </id> <property name="saldo" type="double"> <column name="saldo" /> </property> <property name="datautworzenia" type="timestamp"> <column name="data_utworzenia" /> </property> <property name="dostep" type="java.lang.string"> <column name="dostep" /> </property> </class> </hibernate-mapping>
Tabela na klasę Jedna tabela na klasę Zapis w jednym pliku mapowania Bazowanie na superklasie Definicje <union-subclasses>
Tabela na klasę Konto.java public class Konto { private int kontoid; private double saldo; private Date datautworzenia; KontoROR.java public class KontoROR extends Konto { private String dostep; KontoOszczednosciowe.java public class KontoOszczednosciowe extends Konto { private double procent;
Tabela na klasę <hibernate-mapping> <class name="pl.example.konto" table="konto" abstract="true"> <id name="kontoid" type="int" column="kontoid" > <generator class="hilo" /> </id> <property name="saldo" type="double" column="saldo" /> <property name="datautworzenia" type="java.util.date" column="datautworzenia" /> <union-subclass name="pl.example.kontoror" table="kontoror"> <property name="dostep" type="java.lang.string" column="dostep" /> </union-subclass> <union-subclass name="pl.example.kontooszczednosciowe" table="kontooszczednosciowe"> <property name="procent" type="double" column="procent" /> </union-subclass> </class> </hibernate-mapping>
Przykład session = sf.opensession(); t = session.begintransaction(); KontoOszczednosciowe k1 = new KontoOszczednosciowe(); k1.setdatautworzenia(new Date()); k1.setprocent(10); session.save(k1); KontoROR k2 = new KontoROR(); k2.setdatautworzenia(new Date()); k2.setdostep("ok"); session.save(k2); t.commit(); session.close();
Tabela na hierarchię klas Jedna tabela dla wszystkich subklas Tabela posiada kolumny dla wszystkich możliwych pól Plik mapowania Pojedynczy plik mapowania Definicja <subclass> Używana kolumna, wskazująca na typ klasy
<hibernate-mapping> <class name="pl.example.konto" table="konto" abstract="true"> <id name="kontoid" type="int" column="kontoid"> <generator class="hilo" /> </id> <discriminator column="konto_type" type="string"/> <property name="saldo" type="double" column="saldo" /> <property name="datautworzenia" type="java.util.date" column="datautworzenia" /> <subclass name="pl.example.kontoror" discriminator-value="r"> <property name="dostep" type="java.lang.string" column="dostep" /> </subclass> <subclass name="pl.example.kontooszczednosciowe" discriminator-value="o"> <property name="procent" type="double" column="procent" /> </subclass> </class> </hibernate-mapping>
Tabela na subklasę Jedna tabela dla superklasy i po jednej dla każdej z subklas Kolumny są współdzielone w tabeli superklasy Tabele subklas zawierają charakterystyczne dla nich klumny Mapowane klas Pojedynczy plik mapowania bazujący na superklasie Polecenie <joined-subclass>
Tabela na subklasę <hibernate-mapping> <class name="pl.example.konto" table="konto" abstract="true"> <id name="kontoid" type="int" column="kontoid"> <generator class="identity" /> </id> <property name="saldo" type="double" column="saldo" /> <property name="datautworzenia" type="java.util.date" column="datautworzenia" /> <joined-subclass name="pl.example.kontoror" > <key column="konto_ror_id" /> <property name="dostep" type="java.lang.string" column="dostep" /> </joined-subclass> <joined-subclass name="pl.example.kontooszczednosciowe" > <key column="konto_oszcz_id" /> <property name="procent" type="double" column="procent" /> </joined-subclass> </class> </hibernate-mapping>
Tabela na subklasę