List
Transkrypt
List
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 • <many-to-one name="osoba" column="OSOBA_ID” class="pl.example.Osoba" > • M:M – 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" /> • 1:1 – 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 private private private int id; String imie; String nazwisko; 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", "[email protected]"); cirteria.add(restrictByEmail); Osoba osoba = (Osoba)criteria.uniqueResult(); Inaczej: Osoba osoba = (Osoba)session .createCriteria(Osoba.class) .add(Restriction. eq("email", "[email protected]") ) .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ę