Dostęp do baz danych w aplikacjach Java EE Wykład prowadzi: Marek Wojciechowski JDBC SQLJ Plan wykładu Biblioteka znaczników JSTL SQL EJB (do wersji 2.1) Odwzorowanie obiektowo-relacyjne (O/RM) Java Persistence API Dostęp do baz danych w aplikacjach Java EE JDBC (Java Database Connectivity) Dostęp do baz danych w aplikacjach Java EE (2) JDBC kompletny przykład import java.sql.*; Interfejs programistyczny (API) do współpracy aplikacji Java z relacyjną bazą danych (i innymi źródłami tabelarycznymi) Część Java SE Interfejs dla dynamicznych instrukcji SQL Zaprojektowany na podstawie X/Open SQL Call Level Interface (podobny do ODBC) Interfejs JDBC jest implementowany przez sterowniki JDBC (ang. JDBC drivers), obsługujące poszczególne systemy zarządzania bazą danych Dostęp do baz danych w aplikacjach Java EE (3) public class MyDemo { public static void main(string[] args) throws ClassNotFoundException, SQLException { Connection conn; Statement stmt; ResultSet rset; }} Class.forName("oracle.jdbc.OracleDriver"); conn = DriverManager.getConnection( "jdbc:oracle:thin:@srv1:1521:orcl","scott","tiger"); stmt = conn.createstatement(); rset = stmt.executequery("select ename,sal from emp"); while (rset.next()) { System.out.print(rset.getString("ename")+" "); System.out.println(rset.getString("sal")); } rset.close(); stmt.close(); conn.close(); Dostęp do baz danych w aplikacjach Java EE (4)
Źródła danych w aplikacjach Java EE Mechanizm Connection Pooling Wady otwierania połączeń przez DriverManager parametry połączenia zaszyte w aplikacji kosztowne otwieranie i zamykanie połączeń Od JDBC 2.0 preferowanym mechanizmem uzyskiwania połączeń są źródła danych (obiekty javax.sql.datasource) mogą obsługiwać pule połączeń mogą wspierać transakcje rozproszone posiadają logiczne nazwy, rejestrowane w JNDI w aplikacjach Java EE konfigurowane na serwerze w kodzie aplikacji uzyskany obiekt Connection jest wykorzystywany tak jak przy korzystaniu z DriverManager Dostęp do baz danych w aplikacjach Java EE (5) Konfiguracja źródła danych na serwerze Przykład dla serwera GlassFish <resources> <jdbc-resource enabled="true" jndi-name="jdbc/sample" object-type="user" pool-name="samplepool"/> <jdbc-connection-pool datasource-classname="org.apache.derby.jdbc.clientdatasource" max-pool-size="32" name="samplepool" res-type="javax.sql.datasource" > <property name="databasename" value="sample"/> <property name="user" value="app"/> <property name="password" value="app"/> <property name="url" value="jdbc:derby://localhost:1527/sample"/> <property name="portnumber" value="1527"/> <property name="servername" value="localhost"/> </jdbc-connection-pool> </resources> domain.xml Dostęp do baz danych w aplikacjach Java EE (7) Serwer aplikacji utrzymuje pulę otwartych połączeń z serwerem bazy danych odrębne pule dla różnych parametrów połączenia źródła danych mogą współdzielić tę samą pulę Korzyści: znaczne skrócenie czasu nawiązania połączenia z bazą danych przez aplikację Problemy: potencjalny limit połączeń na poziomie serwera b.d. odpowiedni dobór parametrów puli połączeń (np. min i max liczba połączeń utrzymywanych w puli) Dostęp do baz danych w aplikacjach Java EE (6) Uzyskanie połączenia poprzez źródło Dostęp do źródła poprzez jawny lookup w serwisie nazw JNDI Context ctx = new InitialContext(); DataSource ds = (DataSource)ctx.lookup("jdbc/sample"); Connection conn = ds.getconnection(); Dostęp do źródła dzięki wstrzykiwaniu zależności @Resource(name="jdbc/sample") private DataSource ds; Connection conn = ds.getconnection(); Dostęp do baz danych w aplikacjach Java EE (8)
Problemy z bezpośrednim kodowaniem w JDBC SQLJ Podatne na błędy dynamiczny SQL Uciążliwe konieczność zwalniania zasobów przechwytywanie i obsługa wielu wyjątków Problem niezgodności modeli na poziomie aplikacji (model obiektowy) i bazy danych (model relacyjny) Możliwości uniknięcia kodowania na poziomie JDBC: SQLJ biblioteka znaczników JSTL SQL odwzorowanie obiektowo-relacyjne (O/RM) Dotyczy integracji technologii baz danych z językiem Java: zagnieżdżania instrukcji SQL w kodzie Java (statyczny SQL) wykorzystywania statycznych metod Java jako składowanych procedur i funkcji SQL używania czystych klas Java jako typów danych SQL W pierwszym, podstawowym zakresie funkcjonalności jest standardem praktycznie martwym problemy JDBC, które rozwiązywał straciły na znaczeniu wraz z pojawieniem się O/RM Dostęp do baz danych w aplikacjach Java EE (9) SQLJ przykłady #sql { UPDATE emp SET sal = sal * 1.1 WHERE sal < 1000 }; #sql iterator EmpNameIter(String ename, double sal); EmpNameIter iter; double minsal = 1000.00; #sql iter = {SELECT ename, sal FROM emp WHERE sal > :minsal }; while (iter.next()) { String n = iter.ename(); double s = iter.sal(); System.out.println(n + " " + s); } iter.close(); Dostęp do baz danych w aplikacjach Java EE (11) Dostęp do baz danych w aplikacjach Java EE (10) Biblioteka JSTL SQL Biblioteka znaczników dla JSP do komunikacji z relacyjną bazą danych Koncepcja niezgodna z wzorcem projektowym MVC i architekturą aplikacji wielowarstwowych kod SQL w warstwie (komponentach) prezentacji stosowanie dopuszczalne tylko w małych projektach Dostęp do baz danych w aplikacjach Java EE (12)
JSTL SQL przykład <%@ taglib uri="http://java.sun.com/jstl/core" prefix="c"%> <%@ taglib uri="http://java.sun.com/jstl/sql" prefix="sql"%> <%@ page contenttype="text/html"%> <html><body> <sql:setdatasource var="db" driver="oracle.jdbc.driver.oracledriver" datasource="jdbc/oracleds" url="jdbc:oracle:thin:@miner.cs.put.poznan.pl:1521:miner" user="scott" password="tiger" /> <sql:query var="employees">select * from emp</sql:query> <c:foreach var="columnname" items="${employees.columnnames}"> <th><c:out value="${columnname}"/></th> </c:foreach> <c:foreach var="row" items="${employees.rows}"> <tr><c:foreach var="column" items="${row}"> <td><c:out value="${column.value}"/></td> </c:foreach></tr> </c:foreach> </body></html> Dostęp do baz danych w aplikacjach Java EE (13) Odwzorowanie obiektowo-relacyjne (O/RM) Komponenty encyjne EJB (do wersji 2.1) Technologia EJB od początku J2EE lansowana jako podstawowa technologia implementacja logiki biznesowej; encyjne komponenty EJB jako podstawowy mechanizm komunikacji z bazą danych w aplikacjach EJB Encyjne komponenty EJB nie odniosły sukcesu ( failed technology ) technologia nadmiernie złożona nie wykorzystywały w pełni obiektowych cech Javy nie wykorzystywały siły technologii relacyjnych baz danych (nienaturalne rozwiązania prowadzące do nieefektywnych aplikacji) Dostęp do baz danych w aplikacjach Java EE (14) Java Persistence O/RM = Object-Relational Mapping Implementacje O/RM typowo obejmują: API do zarządzania trwałością obiektów mechanizm specyfikowania metadanych opisujących odwzorowanie klas na relacje w bazach danych język lub API do wykonywania zapytań Popularne implementacje O/RM: Hibernate Oracle Toplink Apache ibatis Java Data Objects (JDO) Dostęp do baz danych w aplikacjach Java EE (15) Standard dotyczący zapewniania trwałości obiektów w aplikacjach Java EE i Java SE opracowany razem z EJB 3 stanowi część Java EE 5 Geneza standardu Java Persistence niepowodzenie koncepcji encyjnych EJB sukces technologii O/RM Rola Java Persistence na tle technologii O/RM oparty o odwzorowanie obiektowo-relacyjne definiuje standardowe API Dostęp do baz danych w aplikacjach Java EE (16)
Elementy standardu Java Persistence Encje Interfejs programistyczny Java Persistence API Język zapytań Java Persistence Query Language Metadane o odwzorowaniu obiektowo-relacyjnym Encja (ang. entity) to lekki obiekt służący do reprezentacji trwałych danych Typowo reprezentuje tabelę z relacyjnej bazy danych Definiowana w formie klasy encji i ewentualnie klas pomocniczych Wymagania dla klas encji: POJO z adnotacją @Entity bezargumentowy konstruktor (public lub protected) implementacja Serializable, jeśli obiekty będą odłączane Dostęp do baz danych w aplikacjach Java EE (17) Encja - Przykład Dostęp do baz danych w aplikacjach Java EE (18) Związki między encjami 1 2 3 4 5 6 @Entity @Table(name="BLEDY") public class Blad implements Serializable { @Id private Long id; private String kod; private String opis; public Blad() { } Blad.java public Long getid() { return id; } public void setid(long id) { this.id = id; } public String getkod() { return kod; } public void setkod(string kod) { this.kod = kod; } public String getopis() { return opis; } public void setopis(string opis) { this.opis = opis; } } Dostęp do baz danych w aplikacjach Java EE (19) Liczność 1:1 (@OneToOne) 1:N (@OneToMany) N:1 (@ManyToOne) N:M (@ManyToMany) Kierunkowość dwukierunkowe jednokierunkowe Kaskada operacji: PERSIST, MERGE, REMOVE, REFRESH, ALL Dostęp do baz danych w aplikacjach Java EE (20)
Związki między encjami - Przykład Zarządca encji (Entity Manager) Wykonawca Album @Entity public class Wykonawca implements Serializable { @OneToMany(cascade=CascadeType.ALL, mappedby="wykonawca") private Collection<Album> albumy; } @Entity public class Album implements Serializable { @ManyToOne private Wykonawca wykonawca; } Dostęp do baz danych w aplikacjach Java EE (21) Jednostka trwałości (Persistence Unit) Zarządca encji zarządzany przez kontener (EJB, JSF) wstrzykiwany do @PersistenceContext komponentu aplikacji EntityManager em; kontekst trwałości propagowany między komponentami w ramach transakcji JTA Zarządca encji zarządzany przez aplikację (serwlety,se) tworzony i niszczony przez aplikację każdy zarządca encji tworzy odrębny kontekst trwałości @PersistenceUnit EntityManagerFactory emf; EntityManager em = emf.createentitymanager(); Dostęp do baz danych w aplikacjach Java EE (22) Plik persistence.xml - Przykład Definiuje zbiór klas encji zarządzanych przez EntityManager w aplikacji Obejmuje klasy encji z jednej bazy danych Definiowana w pliku konfiguracyjnym persistence.xml Posiada nazwę unikalną w zasięgu widzialności W aplikacjach Java EE wykorzystuje źródło danych obsługujące transakcje JTA nieobsługujące transakcji JTA W aplikacjach Java SE zawiera parametry połączenia JDBC 1 2 3 4 5 <?xml version="1.0" encoding="utf-8"?> <persistence version="1.0" > persistence.xml <persistence-unit name="albumyjppu" transaction-type="jta"> <provider> oracle.toplink.essentials.ejb.cmp3.entitymanagerfactoryprovider </provider> <jta-data-source>jdbc/sample</jta-data-source> <properties> <property name="toplink.ddl-generation" value="create-tables"/> </properties> </persistence-unit> </persistence> Dostęp do baz danych w aplikacjach Java EE (23) Dostęp do baz danych w aplikacjach Java EE (24)
Cykl życia encji Utrwalanie instancji encji Instancje encji są zarządzane przez instancję EntityManager Stany instancji encji: nowa (ang. new) zarządzana (ang. managed) odłączona (ang. detached) usunięta (ang. removed) Metoda persist() obiektu EntityManager @PersistenceContext EntityManager em; Blad b = new Blad(); b.setkod("b001"); b.setopis("niedozwolona operacja w module X"); em.persist(b); Dostęp do baz danych w aplikacjach Java EE (25) Odczyt, odświeżanie i synchronizacja instancji encji z bazą danych Dostęp do baz danych w aplikacjach Java EE (26) Strategie zarządzania współbieżnością Odczyt poprzez klucz główny: metoda find() Odświeżenie stanu z bazy danych: metoda refresh() Modyfikacje instancji: metody setxxx() encji Synchronizacja instancji odłączonej: metoda merge() Moment zapisu danych do bazy danych: automatycznie: gdy transakcja jest zatwierdzana jawnie: w wyniku wywołania metody flush() Dostęp do baz danych w aplikacjach Java EE (27) No Locking ("ostrich locking") najpopularniejsza strategia ostatni zapis wygrywa (co gdy operacje UPDATE dotyczyły różnych atrybutów?) Pessimistic Locking blokada zakładana przed rozpoczęciem edycji obiektu odpowiednie gdy przewidywane wiele konfliktów (ale) wymaga utrzymania połączenia z bazą danych na czas edycji, przez co nieodpowiednie dla aplikacji webowych Optimistic Locking założenie, że inne transakcje nie zmienią obiektu między jego odczytem a zapisem wykrywanie zmian i ewentualnie wyjątek zalecane dla współczesnych aplikacji Dostęp do baz danych w aplikacjach Java EE (28)
Blokowanie optymistyczne w JPA Atrybut encji oznaczony adnotacją @Version identyfikuje wersję instancji encji typu całkowitoliczbowego (zalecane) lub etykiety czasowej poza szczególnymi sytuacjami automatycznie obsługiwany przez implementację JPA odpowiadającą atrybutowi kolumnę należy dodać ręcznie do tabeli @Version Long ver; Wyjątek javax.persistence.optimisticlockexception pojawia się gdy w momencie zatwierdzania transakcji okazuje się, że identyfikator wersji w bazie danych zmienił się od chwili odczytu danych przez aplikację typowo wyjątek prezentowany użytkownikowi! Blokowanie optymistyczne w JPA bez atrybutu wersji Mechanizm "field locking" nieuwzględniony w standardzie JPA dostępny w niektórych implementacjach Field locking w EclipseLink/TopLink adnotacja @OptimisticLocking typy blokowania optymistycznego (OptimisticLockingType): VERSION_COLUMN (domyślnie) ALL_COLUMNS CHANGED_COLUMNS SELECTED_COLUMNS (wymienione w atrybucie adnotacji) Dostęp do baz danych w aplikacjach Java EE (29) Blokowanie pesymistyczne w JPA Dostęp do baz danych w aplikacjach Java EE (30) Jawne blokowanie w JPA Brak w wersji 1.0 standardu dostępne w niektórych implementacjach realizowane przez natywne zapytania SELECT FOR UPDATE Uwzględnione w JPA 2.0 w dalszym ciągu niezalecane w aplikacjach webowych Stosowane do zablokowania obiektu, który nie ulegnie zmianie np. obiektu powiązanego z modyfikowanym obiektem Realizowane poprzez EntityManager.lock() API em.lock(employee, LockModeType.WRITE) przekazanie parametru LockModeType do metod find() i refresh() EntityManager ustawienie lockmode dla Query / NamedQuery Dostęp do baz danych w aplikacjach Java EE (31) Dostęp do baz danych w aplikacjach Java EE (32)
Tryby blokowania (JPA 2.0) Usuwanie instancji encji OPTIMISTIC (READ w JPA 1.0) OPTIMISTIC_FORCE_INCREMENT (WRITE w JPA 1.0) PESSIMISTIC_READ PESSIMISTIC_WRITE PESSIMISTIC_FORCE_INCREMENT NONE Metoda remove() obiektu EntityManager @PersistenceContext EntityManager em; Blad b = em.find(blad.class, new Long(13)); em.remove(b); Dostęp do baz danych w aplikacjach Java EE (33) Transakcje Dostęp do baz danych w aplikacjach Java EE (34) Zapytania do bazy danych EntityManager zarządzany przez kontener (EJB) kontener zarządza transakcjami EntityManager zarządzany przez aplikację transakcja JTA (serwlety) @Resource UserTransaction utx; utx.begin(); utx.commit(); // utx.rollback(); transakcja na poziomie zasobu (Java SE, serwlety) EntityManager em; em.gettransaction().begin(); em.gettransaction().commit(); // em.gettransaction().rollback(); Dostęp do baz danych w aplikacjach Java EE (35) Rodzaje zapytań (metody obiektu EntityManager) dynamiczne w JPQL - createquery() dynamiczne natywne - createnativequery() nazwane (JPQL lub natywne) - createnamedquery() Parametryzacja zapytań nazwane (np. :kodbledu) pozycyjne (np.?1) Wykonanie zapytania (metody obiektu Query) getresultlist(), getsingleresult() executeupdate() Dostęp do baz danych w aplikacjach Java EE (36)
Zapytanie dynamiczne - Przykład Zapytanie nazwane - Przykład Blad.java EntityManager em; List<Blad> wyn = null; Query q = em.createquery( "SELECT b FROM Blad b WHERE b.opis LIKE '%problem%'"); wyn = q.getresultlist(); @Entity @NamedQuery(name = "findbykeyword", query = "SELECT b FROM Blad b WHERE b.opis LIKE :keyword") public class Blad implements Serializable { } EntityManager em; List<Blad> wyn = null; wyn = em.createnamedquery("findbykeyword").setparameter("keyword", "%krytyczny%").getresultlist(); Dostęp do baz danych w aplikacjach Java EE (37) Java Persistence Query Language (JPQL) Dostęp do baz danych w aplikacjach Java EE (38) JPQL - Przykłady Umożliwia formułowanie przenaszalnych zapytań, niezależnych od specyfiki poszczególnych systemów Zapytania operują na abstrakcyjnym schemacie obejmującym encje i związki między nimi Składnia podobna do SQL: zapytania SELECT-FROM-WHERE-GROUP BY- HAVING-ORDER BY polecenia UPDATE i DELETE dla masowych operacji modyfikacji i usuwania: UPDATE-SET-WHERE, DELETE-FROM-WHERE Wyrażenia ścieżkowe do nawigacji do związanych encji Nawigacja do kolekcji powiązanych instancji encji SELECT DISTINCT w FROM Wykonawca w, IN(w.albumy) a Wyrażenie ścieżkowe SELECT DISTINCT w FROM Wykonawca w JOIN w.albumy a SELECT a FROM Album a WHERE a.wykonawca.nazwa = Mandaryna Dostęp do baz danych w aplikacjach Java EE (39) Dostęp do baz danych w aplikacjach Java EE (40)
Podsumowanie Materiały dodatkowe Na niskim poziomie aplikacje Java korzystają w komunikacji z bazami danych z interfejsu JDBC Bezpośrednie kodowanie w JDBC jest uciążliwe i podatne na błędy, a ponadto nie wykorzystuje obiektowych cech języka Java Dostęp do baz danych w aplikacjach języka Java operujących na złożonym obiektowym modelu biznesowym obecnie realizowany jest najczęściej w oparciu o technologie odwzorowania obiektoworelacyjnego (O/RM) Java Persistence to standard oparty o odwzorowanie obiektowo-relacyjne, definiujący standardowe API Dostęp do baz danych w aplikacjach Java EE (41) JDBC, http://java.sun.com/javase/technologies/database/ Hibernate, http://www.hibernate.org/ The Java EE 5 Tutorial, http://java.sun.com/javaee/5/docs/tutorial/doc/ Dostęp do baz danych w aplikacjach Java EE (42)