Programowanie w języku Java WYKŁAD dr inż. Piotr Zabawa Certyfikowany Konsultant IBM/Rational e-mail: pzabawa@pk.edu.pl www: http://www.pk.edu.pl/~pzabawa 26.05.2014
WYKŁAD 13 Refleksja Data Access Object (DAO)
Refleksja
Refleksja Zakład Inżynierii Oprogramowania Refleksja jest jedną z cech charakterystycznych języka Java. Jednak wsparcie ze strony tego języka dla refleksji jest dość ograniczone charakter tych ograniczeń zostanie przedstawiony na kolejnych slajdach na tle cech charakterystycznych refleksji.
Refleksja Zakład Inżynierii Oprogramowania Jest to pojęcie z dziedziny informatyki oznaczające proces, dzięki któremu program komputerowy może być modyfikowany w trakcie działania w sposób zależny od własnego kodu oraz od zachowania w trakcie wykonania. Paradygmat programowania ściśle związany z mechanizmem refleksji to programowanie refleksyjne. Refleksja jest więc cechą interfejsu programistycznego i techniką programistyczną posiadającą dwa aspekty: Możliwość dynamicznego uzyskania podczas wykonywania programu jego meta-danych, np. struktury klas, dostępnych metod w klasie, typów argumentów metod, informacji o nadklasach danego obiektu, interfejsów do obiektów Możliwość wykorzystania powyższych informacji w programie np. przez utworzenie w czasie wykonania stringu zawierającego kod do wykonania w tym samym programie w czasie tej samej sesji programu
Refleksja Zakład Inżynierii Oprogramowania Refleksja jest jednym z mechanizmów wykorzystywanych w dynamicznych językach programowania, takich jak np. Groovy Scala Ruby (w tym JRuby) Eiffel Erlang Lisp Perl Prolog Pyton (w tym Jyton) Smalltalk Clojure
Refleksja Zakład Inżynierii Oprogramowania W przypadku języka Java należy wziąć pod uwagę ograniczenie polegające na zjawisku zacierania typów (wspomniane na wykładzie o typach uogólnionych i o kontenerach). Zjawisko to osłabia mechanizm refleksji. Kolejne ograniczenie dotyczyło możliwości wstrzykiwania kodu do wersji Java 7. Ograniczenie to zostało jednak w znacznym stopniu zniesione za pomocą wyrażeń Lambda w Java 8. W ten sposób znacznie osłabiono problemy w Java związane z drugim aspektem refleksji. Jest to zarazem znaczący krok w stronę tzw. języków dynamicznych.
Refleksja Zakład Inżynierii Oprogramowania Mechanizm refleksji został zaimplementowany w bibliotekach standardowych Java w pakietach: java.lang.reflect java.lang
Refleksja dynamiczne ładowanie klas Wszystkie klasy Java dziedziczą z klasy Object. Dlatego właśnie możliwe było wprowadzenie mechanizmu refleksji. Każdy obiekt ma metodę getclass() zaimplementowaną w klasie Object. Zwraca ona informację o klasie danego obiektu. Informacja ta znajduje się w obiekcie java.lang.class, a wspomniana metoda zwraca referencję do tego obiektu. Po uzyskaniu referencji do tego obiektu można na nim wykonywać szereg operacji, np. getsuperclass() - zwracającą obiekt klasy Class oznaczający klasę bazową danej klasy, getinterfaces() - zawracającą tablicę obiektów, zawierającą interfejsy danej klasy, newinstance() - tworzącą nowy obiekt danej klasy. Zakład Inżynierii Oprogramowania
Refleksja dynamiczne ładowanie klas Warto zwrócić uwagę na metodę newinstance() jedyną, za pomocą której możemy tworzyć nowe obiekty w ramach mechanizmu refleksji. Nie możemy korzystać z operatora new().
Refleksja Zakład Inżynierii Oprogramowania Podstawową metodą umożliwiającą realizowanie wywołań metod w ramach mechanizmu refleksji jest invokemethod(), której argumentami są obiekt, na którym ma zostać wywołana metoda oraz argumenty wywoływanej metody. Przykład. Statyczne wywołanie: o.getdata(); Dynamiczne wywołanie: o.invokemethod("getdata");
Refleksja Zakład Inżynierii Oprogramowania Klasy pakietu java.lang.reflect Array Constructor uzyskiwanie informacji i dostęp do konstruktora danej klasy przydatne przy dynamicznym tworzeniu obiektu Field informacja i dostęp do wartości pola Method informacja o metodzie danej klasy i dynamiczne jej wywołanie na rzecz wskazanego obiektu Modifier infomacja o modyfikatorach składowej obiektu lub klasy
Refleksja Zakład Inżynierii Oprogramowania Przydatne metody klasy java.lang.class getclasses()/getdeclaredclasses() zwracają tablicę obiektów klasy Class, które są składowymi danej klasy getconstructors()/getdeclaredconstructors() zwracają tablicę obiektów klasy Constructor; są to konstruktory danej klasy getconstructor(class[])/getdeclaredconstructor(class[]) - zwracają obiekt konstruktor (obiekt klasy konstruktor), który ma podane typy argumentów getmethods()/getdeclaredmethods() - zwracają tablicę, zawierającą odnośniki do metod klasy. Metody są obiektami klasy Method. getmethod(string, Class[])/getDeclaredMethod(String, Class[]) - zwracają metodę o podanej nazwie i podanych argumentach jako obiekt klsy Method.
Refleksja Zakład Inżynierii Oprogramowania Uwaga: Metody z Declared zwracają wszystkie metody zadeklarowane w danej klasie (wraz z prywatnymi) ale bez dziedziczonych Metody bez Declared zwracają wszystkie metody publiczne wraz z publicznymi dziedziczonymi
Refleksja Zakład Inżynierii Oprogramowania Przykład zastosowania mechanizmu refleksji podany na stronie internetowej wykładu.
Dalej wykorzystano w znacznym stopniu: http://tutorials.jenkov.com/java-persistence/index.html oraz: http://www.eti.pg.gda.pl/katedry/kask/pracownicy/jarosl aw.kuchta/pai/laboratorium_nr_6.pdf
Jest to wzorzec architektoniczny wspierający operacje na warstwie danych. Wprowadza do oprogramowania dodatkową warstwę odpowiedzialną za dostęp do danych. Został wprowadzony najpierw do Java EE przez firmę Sun, jest jednak dostępny w większości technologii.
Wzorzec DAO służy do odseparowania informacji o danych aplikacji od sposobu, w jaki zapewniany jest dla nich mechanizm trwałości. Innymi słowy, aplikacja (jej warstwa logiki biznesowej) odwołuje się do danych w sposób niezależny od tego jak ani czy zapewniono ich trwałość. Zastosowanie takiego wzorca pozwala w konsekwencji na zmianę technologii zapewniania trwałości. W przypadku zastosowania technologii Hibernate wraz z DAO warstwa logiki biznesowej nie wie o fakcie zastosowania Hibernate do zapewnienia trwałości. Zmiana technologii trwałości dzięki użyciu DAO zawęża obszar zmian w kodzie do klas warstwy DAO i nie ma wpływu na warstwę logiki biznesowej. W praktyce zapewnienie aż tak daleko idącego uniezależnienia warstwy logiki biznesowej od mechanizmu trwałości może okazać się trudne do osiągnięcia. Dalej scharakteryzowano trudności.
Zakresy połączeń DAO (DAO connection scope) Jakie są możliwe relacje pomiędzy elementami DAO i połączeniami z bazą danych: Zakres metody każda metoda zarządza swoim połączeniem Zakres instancji każdy obiekt DAO zarządza swoim połączeniem Zakres wątku cała grupa obiektów DAO używanych przez dany wątek dla danej sesji z bazą danych zarządza swoim wątkiem
Zakres metody public class PersonDao{ protected DataSource datasource = null; public PersonDao(DataSource datasource){ this.datasource = datasource; public Person readperson(long personid){ Connection connection = this.datasource.getconnection(); try{ Person person =... return person; finally { connection.close();
Problemy: Wywołanie jednej metody DAO w drugiej marnowanie połączeń Jeśli dwie metody mają być wykonane w tej samej transakcji, to muszą używać tego samego połączenia jak to zapewnić w tym podjściu?
Zakres instancji public class PersonDao{ protected Connection connection = null; public PersonDao(Connection connection){ this.connection = connection; public Person readperson(long personid){ Person person =... return person;
Problemy: Warstwa DAO nie wie jak zarządzać otwieraniem/zamykaniem połączeń Warstwa logiki biznesowej nie powinna zajmować się otwieraniem/zamykaniem połączeń, ponieważ ma być od nich niezależna Możliwość zastosowania wzorca projektowego Metody Wytwórczej do otwierania połączeń, ale każdy obiekt DAO zamyka połączenie: PersonDao persondao = daofactory.createpersondao(); Person person = persondao.readperson(666);... persondao.close(); Co zrobić gdy musimy zapewnić wielu obiektom DAO skorzystanie z tego samego połączenia?
Zakres wątku daofactory.beginconnectionscope(); PersonDao persondao = daofactory.createpersondao(); VehicleDao vehicledao = daofactory.createvehicledao(); //... daofactory.endconnectionscope();
Transakcje w DAO Obsługę transakcji można w naturalny sposób wpasować w realizację DAO z zakresem wątku. daofactory.beginconnectionscope(); daofactory.begintransaction(); PersonDao persondao = daofactory.createpersondao(); VehicleDao vehicledao = daofactory.createvehicledao(); //... daofactory.endtransaction(); daofactory.endconnectionscope();
W ramach jednego połączenia można zrealizować wiele transakcji. Spośród przedstawiony powyżej rozwiązań zarówno zakres wątku jak i zakres transakcji pozwalają ukryć przed warstwą logiki biznesowej sposób realizacji warstwy trwałości. Jednak nadal pozostają nierozwiązane dwa problemy: Demarcation of the connection Zakresy transakcji Ich rozwiązanie zostanie przedstawione w ramach DAOManager.
Obsługa wyjątków DAO W rozwiązaniach podanych wyżej jako zakres wątku lub zakres transakcji zgłoszenie wyjątku w jednej z metod DAO powoduje nieprawidłowe zachowanie aplikacji. Prawidłowy sposób obsługi wyjątków podano na następnym slajdzie.
try{ daofactory.beginconnectionscope(); try{ daofactory.begintransaction(); PersonDao persondao = daofactory.createpersondao(); VehicleDao vehicledao = daofactory.createvehicledao(); daofactory.endtransaction(); catch(exception e){ daofactory.aborttransaction(e); finally { daofactory.endconnectionscope();
DAOManager Pozwala rozwiązać problemy nie rozwiązane wcześniej z wykorzystaniem fabryk.
public class DaoManager{ protected Connection connection = null; protected PersonDao persondao = null; public DaoManager(Connection connection){ this.connection = connection; public PersonDao getpersondao(){ if(this.persondao == null){ this.persondao = new PersonDao(this.connection); return this.persondao;
Zakres połączenia DAOManager Sposób użycia w warstwie logiki biznesowej: DaoManager daomanager = daofactory.createdaomanager(); Person person = daomanager.getpersondao().readperson(666); Jak widać nie wykonano operacji close(). Aby ją zaimplementować warto zastosować wzorzec projektowy Metoda szablonowa. Jego realizację przedstawiono dalej.
public class DaoManager{... public Object executeandclose(daocommand command){ try{ return command.execute(this); finally { this.connection.close();
public interface DaoCommand { public Object execute(daomanager daomanager); Sposób korzystania: DaoManager daomanager = daofactory.createdaomanager(); Person person = (Person) daomanager.executeandclose(new DaoCommand(){ public Object execute(daomanager manager){ return manager.getpersondao().readperson(666); );
Zakres transakcji DAOManager Podobnie jak dodano do DAOManager a metodę zarządzania połączeniami, można dodać metodę zarządzania transakcjami i ponownie wykorzystać tym razem dla transakcyjności wzorzec projektowy metoda szablonowa. Dodanie tej metody pokazano na następnym slajdzie, jednak z obsługą wyjątków bardzo uproszczoną i nie działającą poprawnie w niektórych sytuacjach.
public class DaoManager{... public Object transaction(daocommand command){ try{ this.connection.setautocommit(false); Object returnvalue = command.execute(this); this.connection.commit(); return returnvalue; catch(exception e){ this.connection.rollback(); throw e; //or wrap it before rethrowing it finally { this.connection.setautocommit(true);
Sposób wykorzystania tej metody: DaoManager daomanager = daofactory.createdaomanager(); daomanager.transaction(new DaoCommand(){ public Object execute(daomanager manager){ Person person = manager.getpersondao().readperson(666); ); person.setlastname("nick"); manager.getpersondao().updateperson(person);
Zamknięcie połączenia można zrealizować przez analogię do poprzedniego rozwiązania: public class DaoManager{ //... public Object transactionandclose(daocommand command){ executeandclose(new DaoCommand(){ public Object execute(daomanager manager){ ); manager.transaction(command);
Podsumowanie DaoManager daomanager = daofactory.createdaomanager(); daomanager.transaction(new DaoCommand(){ public Object execute(daomanager manager){ Person person = manager.getpersondao().readperson(666); person.setlastname("nick"); manager.getpersondao().updateperson(person); );
Problemy rozwiązane za pomocą DAOManager a: Zaznaczenie zakresu życia połączenia Zaznaczenia zakresu transakcji Automatyzacja otwierania/zamykania połaczenia Automatyzacja zatwierdzania/odrzucania transakcji Centralizacja obsługi wyjątków Centralizacja dostępu do DAO ułatwienie identyfikacji wszystkich DAO lub ułatwienie ich ponownego użycia
Wykorzystanie DAO we frameworkach Javy: Hibernate Spring http://docs.spring.io/spring/docs/3.0.x/api/org/springframework/dao/su pport/daosupport.html Apache Hibernate Generic DAO: https://code.google.com/p/hibernate-generic-dao/
Generalizacje DAO. Wiele implementacji DAO opiera się na typach generycznych parametryzowanych klasą DAO. Podejście takie uważane jest za dobrą praktykę.
Wzorce danych
Wzorce danych Zakład Inżynierii Oprogramowania Istnieje wiele wzorców związanych ze współpracą warstwy logiki biznesowej z warstwą danych. Są to wzorce architektoniczne lub architektoniczne wzorce korporacyjne. Warto zwrócić uwagę na dużo mniejsze uporządkowanie i usystematyzowanie tych wzorców w porównania z wzorcami projektowymi.
Wzorce danych Zakład Inżynierii Oprogramowania Martin Fowler [http://martinfowler.com/eaacatalog/]: Data Transfer Object (DTO) http://martinfowler.com/eaacatalog/datatransferobject.html Active Record http://martinfowler.com/eaacatalog/activerecord.html Data Mapper http://martinfowler.com/eaacatalog/datamapper.html Table Data Gateway http://martinfowler.com/eaacatalog/tabledatagateway.html Row Data Gateway http://martinfowler.com/eaacatalog/rowdatagateway.html
Wzorce danych Zakład Inżynierii Oprogramowania Core J2EE patterns Data Access Objects (DAO) Transfer Object Value List Handler Domain Store http://www.corej2eepatterns.com/domainstore.htm Eneterprise Integration Patterns http://www.eaipatterns.com/
Koniec