Java EE: JSF + EJB + JPA Celem ćwiczenia jest utworzenie prostej aplikacji bazodanowej umożliwiającej przeglądanie i dodawanie zleceń serwisowych. Ćwiczenie pokazuje współpracę technologii JSF (podstawowej technologii implementacji interfejsu użytkownika na platformie Java EE) z technologiami EJB (podstawową technologią implementacji logiki biznesowej) i JPA (standardową technologią odwzorowania obiektowo-relacyjnego, stanowiącego dominujące obecnie podejście do obsługi komunikacji z bazą danych). Do realizacji ćwiczenia wykorzystane zostało środowisko NetBeans 7.2 wraz z serwerem aplikacji GlassFish v3.1.2 i serwerem bazy danych Java DB. 1. Uruchom środowisko NetBeans. 2. Utwórz nowy projekt typu Enterprise Application: a. Wybierz z menu opcję File->New Project. b. W pierwszym kroku kreatora jako typ projektu wybierz Enterprise Application z kategorii Java EE. c. W drugim kroku kreatora jako nazwę projektu podaj Requests, a pozostałe ustawienia w tym oknie pozostaw domyślne. d. W ostatnim kroku kreatora jako serwer wybierz GlassFish v3.1.2 i Java EE 6 jako wersję Javy EE. Upewnij się czy zaznaczone jest tworzenie dwóch typów modułów: EJB i webowego. Pozostaw zaproponowane przez kreator nazwy modułów. e. Po zakończeniu działania kreatora obejrzyj organizację aplikacji w oknie Projects. Zwróć uwagę, że w środowisku NetBeans moduły składowe aplikacji Enterprise Application stanowią odrębne projekty, a główny projekt łączy je w całość (inne środowiska IDE mogą stosować inną organizację kodu). 3. Korzystając z menu kontekstowego przejdź do edycji właściwości projektu modułu webowego. Dodaj do projektu framework JavaServer Faces.
4. Uruchom serwer bazy danych Java DB korzystając z panelu Services. 5. Z poziomu panelu Services połącz się z bazą danych sample na lokalnym serwerze Java DB (derby). 6. Utwórz w bazie danych tabelę, na której działać będzie tworzona aplikacja, realizując poniższe kroki: a. Wywołaj okno poleceń SQL dla bazy danych sample wybierając z menu kontekstowego dla węzła reprezentującego połączenie opcję Execute Command. b. Korzystając z okna poleceń SQL wykonaj kolejno poniższe polecenia SQL (pierwsze trzy mogą zakończyć się błędami służą do ewentualnego usunięcia istniejących już tabel):
DROP TABLE requests; DROP TABLE employees; DROP TABLE departments; CREATE TABLE requests (id INT NOT NULL GENERATED ALWAYS AS IDENTITY CONSTRAINT requests_pk PRIMARY KEY, request_date DATE, request_text VARCHAR(60) NOT NULL); CREATE TABLE departments (id INT NOT NULL GENERATED ALWAYS AS IDENTITY CONSTRAINT dept_pk PRIMARY KEY, department_name VARCHAR(60) NOT NULL); CREATE TABLE employees (id INT NOT NULL GENERATED ALWAYS AS IDENTITY CONSTRAINT emp_pk PRIMARY KEY, employee_name VARCHAR(60) NOT NULL, salary DECIMAL(8,2) NOT NULL, department_id INT REFERENCES departments(id)); INSERT INTO requests(request_date, request_text ) VALUES (CURRENT_DATE, 'Please check TV in room 242'); INSERT INTO requests(request_date, request_text ) VALUES (CURRENT_DATE, 'Repair fridge in room 311'); c. Upewnij się odpowiednim zapytaniem, że faktycznie zostały dodane dwa wiersze do tabeli REQUESTS. 7. W projekcie EJB utwórz jednostkę trwałości, w ramach której obiekty aplikacji będą zachowywane w bazie danych. W tym celu: a. Kliknij lewym przyciskiem myszy na ikonie projektu EJB w panelu projektów i z menu kontekstowego wybierz File->New File. Następnie z kategorii Persistence wybierz typ pliku Persistence Unit. Kliknij przycisk Next.
b. W drugim kroku kreatora zmień nazwę jednostki trwałości na RequestsPU, a z listy dostępnych źródeł danych wybierz jdbc/sample. Dla pozostałych opcji pozostaw ustawienia domyślne. Kliknij przycisk Finish. 8. Utworzenie za pomocą kreatora zbioru encji na podstawie istniejących tabel w bazie danych. a. Kliknij prawym przyciskiem myszy na ikonie projektu EJB w drzewie projektów i z menu kontekstowego wybierz New->Entity Classes from Database. b. Z listy dostępnych źródeł danych wybierz jdbc/sample. c. Następnie na liście tabel do wyboru w lewym panelu, przy zaznaczonym polu wyboru Include related tables, zaznacz myszą tabele REQUESTS i EMPLOYEES i kliknij przycisk Add. Zauważ, że na liście wybranych tabel (w prawym panelu) automatycznie została uwzględniona tabela DEPARTMENTS, do której odwołuje się klucz obcy z tabeli EMPLOYEES. Kliknij przycisk Next.
d. W kolejnym kroku kreatora jako nazwę pakietu podaj req.entities. Zmień zaproponowane nazwy klas encji na liczbę pojedynczą (Department, Employee, Request). W pozostałych polach pozostaw wartości domyślne. Kliknij Next. e. W ostatnim kroku kreatora sprawdź jakie wartości są dostępne dla opcji Association Fetch i Collection Type. Pozostaw wartości zaproponowane przez kreator i kliknij Finish.
f. Obejrzyj kod wygenerowanej klasy Request. Upewnij się, że rozumiesz znaczenie wszystkich adnotacji. g. Odszukaj w wygenerowanych klasach Employee i Department pola i ich adnotacje odpowiedzialne za dwukierunkową nawigację między obiektami encji. Zwróć uwagę, że encja Employee udostępnia powiązany obiekt Department, a nie wartość klucza obcego. 9. W projekcie EJB utwórz nowy bezstanowy komponent sesyjny EJB 3.x, który będzie pełnił funkcję fasady udostępniającej operacje na obiektach trwałych. a. Z menu kontekstowego projektu EJB uruchom kreator Session Beans For Entity Classes z kategorii Persistence. b. Wskaż, że tworzony komponent fasadowy ma obsługiwać tylko encję Request i kliknij Next.
c. W ostatnim kroku kreatora zmień proponowany pakiet dla klasy komponentu na req.facade. Zaznacz, że komponent ma być dostępny tylko przez interfejs lokalny. Kliknij Finish. d. W klasie komponentu fasadowego podmień definicję metody findall(), tak aby wykorzystywała zdefiniowane dla klasy encji zapytanie nazwane (odszukaj nazwę tego zapytania nazwanego i wpisz je w miejsce wielokropka w poniższej instrukcji, która ma stanowić ciało metody findall()). return getentitymanager().createnamedquery("...").getresultlist(); 10. W projekcie webowym uruchom kreator tworzenia nowej strony JSF (JSF Page z kategorii JavaServer Faces). Jako nazwę strony wprowadź requestslist (odpowiednie rozszerzenie nawy pliku zostanie nadane automatycznie). Pozostałe opcje pozostaw domyślne (zwróć uwagę na wybór składni Facelets, nie JSP) i kliknij Finish. 11. Utwórz komponent backing bean obsługujący stronę JSF. W tym celu:
a. Uruchom w projekcie webowym kreator JSF Managed Bean z kategorii JavaServer Faces. b. Jako nazwę klasy komponentu podaj RequestsList, a jako nazwę pakietu req.backing. Pozostaw wybrany zasięg request typowy dla komponentów backing bean. Kliknij Finish. 12. Przejdź do edycji kodu klasy komponentu zarządzanego. Wprowadź w nim poniższe modyfikacje: a. Wywołaj menu kontekstowe klikając prawym klawiszem myszy wewnątrz klasy przed jej konstruktorem. Z menu wybierz Insert Code->Call Enterprise Bean. W oknie dialogowym z dostępnymi komponentami zaznacz komponent fasadowy. Skoryguj zaproponowaną nazwę referencji, tak aby zaczynała się z małej litery i wybierz korzystanie z interfejsu lokalnego. Kliknij OK.
b. Poniżej konstruktora wklej poniższą metodę, która ma pośredniczyć w dostępie do metody komponentu fasadowego i udostępniać listę wszystkie obiekty encji w formie właściwości komponentu zarządzanego. Samodzielnie uzupełnij kod metody i zaimportuj wykorzystywane klasy. Zapisz wszystkie zmiany. public List<Request> getallrequests() {... } 13. Przejdź do edycji źródła strony JSF. Usuń treść zawartą w elemencie <h:body>. Następnie techniką drag-and-drop z palety komponentów umieść na stronie wewnątrz elementu <h:body> komponent JSF Data Table From Entity. W oknie dialogowym, które pojawi się po upuszczeniu komponentu na stronie wybierz encję Request i właściwość komponentu backing bean udostępniającą wszystkie instancje encji Request. 14. Obejrzyj kod wygenerowany przez kreator i dokonaj następujących poprawek: a. Usuń źle umiejscowione znaczniki <f:view> i </f:view> (pozostawiając zawartość między nimi!) b. W znaczniku <html> dodaj atrybut dla przestrzeni nazw znaczników JSF Core wskazując f jako prefiks dla znaczników z tej przestrzeni. <html xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html"> c. Dodaj w znaczniku <h:datatable> atrybut border z wartością 1.
d. Usuń nagłówek <h1> (wraz z zawartością) poprzedzający element <h:datatable>. e. Zmień format daty w komponencie formatującym <f:convertdatetime> na yyyy-mm-dd. 15. Wyświetl właściwości głównego projektu aplikacji. Wybierz kategorię Run i w sekcji Web Module Information jako względny adres startowy podaj /faces/requestslist.xhtml. Kliknij OK. 16. Zapisz wszystkie zmiany i uruchom główny projekt aplikacji. 17. Wróć do edycji źródła utworzonej strony JSF i wstaw wewnątrz elementu <h:form> przed elementem <h:datatable> poniższe trzy elementy (korzystając z podpowiedzi i autouzupełniania edytora tekstowego): <h:outputtext> z atrybutem value o wartości New request, <h:inputtext> z atrybutem id o wartości newreqinputtext, <h:commandbutton> z atrybutem value o wartości Add. 18. Przejdź do edycji kodu klasy komponentu backing bean. Wywołaj poprzez menu kontekstowe edytora kreator Insert Code->AddProperty. Dodaj prywatną właściwość newrequest typu String z publicznymi metodami getter i setter.
19. Analogicznie do poprzedniego punktu ćwiczenia dodaj w klasie backing bean właściwość requestsdatatable typu javax.faces.component.html.htmldatatable. 20. Przejdź do edycji strony JSF. Dokonaj powiązania jej komponentów z komponentem backing bean dodając następujące atrybuty do wskazanych elementów: <h:inputtext value="#{requestslist.newrequest}>", <h:commandbutton action="#{requestslist.addrequest}>", <h:datatable> binding="#{requestslist.requestsdatatable}>". 21. Dodaj w klasie komponentu backing bean metodę, do której odnosi się przycisk na stronie JSF. Uzupełnij kod metody o operacje: utworzenia instancji klasy Request, ustawienia w nowo utworzonym obiekcie daty zlecenia na bieżącą i tekstu zlecenia na wprowadzony do formularza, utrwalenia obiektu poprzez fasadę. public String addrequest() {... setnewrequest(""); return null; }
22. Zapisz wszystkie zmiany. Uruchom aplikację. Przetestuj dodawanie nowych zleceń serwisowych. 23. Dodaj w klasie komponentu backing bean metodę do usuwania obiektu encji Request reprezentowanego przez bieżący wiersz w komponencie Data Table. Uzupełnij kod metody o operację usunięcia trwałej reprezentacji obiektu za pomocą fasady. public String deleterequest() { Request req = (Request) getrequestsdatatable().getrowdata();... return null; } 24. Przejdź do edycji strony JSF. Dodaj w komponencie Data Table kolumnę z linkami umożliwiającymi usunięcie zlecenia wyświetlanego w bieżącym wierszu (jako ostatnią kolumnę tabeli). 25. Zapisz wszystkie zmiany. Uruchom aplikację i przetestuj jej działanie usuwając kilka zleceń.