Encyjne komponenty EJB
Transkrypt
Encyjne komponenty EJB
132 Encyjne komponenty EJB Marek Wojciechowski [email protected] http://www.cs.put.poznan.pl/mwojciechowski/ 133 Plan rozdziału • • • • • • • Wprowadzenie do encyjnych komponentów EJB Cykl życia encyjnego EJB Tworzenie encyjnego EJB w środowisku Oracle JDeveloper Wykorzystywanie encyjnych EJB Powiązania między encyjnymi EJB Transakcje zarządzane przez kontener Dostęp do encyjnych EJB z poziomu JSP 134 Encyjne komponenty EJB • Reprezentują obiekty składowane w bazie danych – Na platformie J2EE podstawowym mechanizmem trwałego przechowywania danych jest relacyjna baza danych – Przykłady trwałych obiektów: pracownicy, oddziały, produkty, ... • Nie modelują procesów, nie zawierają skomplikowanej logiki biznesowej, ale zarządzają danymi wykorzystywanymi w procesach biznesowych – Mogą zawierać kod np. walidujący dane • Typowo każdy komponent encyjny jest oparty o tabelę w relacyjnej bazie danych, a każda instancja komponentu encyjnego reprezentuje wiersz w tabeli – Dane z tabel przedstawiane w formie obiektów Instancje encyjnych EJB a dane w bazie danych • Wiele instancji encyjnego komponentu EJB może w danej chwili reprezentować te same dane (ten sam wiersz tabeli) – Synchronizacja instancji komponentu z bazą danych jest obsługiwana przez kontener • Dana instancja encyjnego komponentu EJB nie musi przez cały czas swego istnienia reprezentować tych samych danych (!) – Kontener utrzymuje pulę (pool) instancji komponentu – W czasie swego istnienia instancje są aktywowane (pobierane z puli) i pasywowane (zwracane do puli) – Aktywowane instancje są dynamicznie przypisywane do reprezentacji instancji danych z bazy danych (w kolejnych "okresach aktywności" instancja komponentu może reprezentować różne obiekty tego samego typu) 135 136 Cechy encyjnych komponentów EJB • Trwałość – Składowane w bazie danych (typowo) – Odporne na awarie aplikacji, serwera aplikacji, serwera bd – Trwałość zarządzana przez kontener (CMP) lub komponent (BMP) • Współdzielenie komponentu przez wielu klientów – Działanie w ramach transakcji (zarządzanych przez kontener: CMT) – Możliwość współdzielenia instancji komponentu przez kilku klientów lub korzystania z różnych instancji reprezentujących te same dane • Klucz główny (primary key) – Unikalny identyfikator obiektu – Podstawowe kryterium wyszukiwania obiektów • Związki – Powiązania z innymi komponentami encyjnymi – Analogia do związków między tabelami w relacyjnej bazie danych 137 Trwałość zarządzana przez kontener • CMP - Container-Managed Persistence • Kontener EJB obsługuje wszystkie operacje na bazie danych, których wymaga komponent • Kod komponentu nie zawiera żadnych instrukcji SQL • Przenaszalność dzięki niezależności od konkretnej bazy danych • Kontener generuje odwołania do bazy danych w oparciu o abstrakcyjny schemat podany w deskryptorach instalacji • Kontener EJB obsługuje również związki między komponentami encyjnymi – CMR – Container-Managed Relationships 138 Trwałość zarządzana przez komponent • BMP - Bean-Managed Persistence • Twórca komponentu dostarcza kod zarządzający trwałością komponentu: – Implementując metody callback do odczytu, zapisu i manipulacji danymi oraz zarządzania związkami np. ejbLoad(), ejbStore(), ejbCreate(), ejbPostCreate(), ejbRemove(), ejbFindXXX() – Kod komponentu zawiera wywołania JDBC / SQLJ • Ograniczona przenaszalność – kod odwołuje się do konkretnego schematu bazy danych • Elastyczność – twórca komponentu ma "pełną władzę" nad zarządzaniem jego trwałością – Niekoniecznie komponent musi odpowiadać jednej tabeli – Dane mogą być składowane np. w LDAP, a nie tylko w bazie danych! 139 CMP czy BMP? CMP BMP 140 Składniki encyjnego komponentu EJB • Interfejs domowy (Home / LocalHome) – Wykorzystywany przez klientów do tworzenia, usuwania i wyszukiwania obiektów encyjnych • Interfejs komponentowy (Remote / Local) – Zawiera deklaracje metod biznesowych komponentu, udostępnianych klientom • Klasa komponentu – Reprezentuje dane – Zawiera metody dostępu do danych i manipulacji danymi • Klasa klucza głównego – Wykorzystywana do identyfikacji instancji komponentu • Deskryptor instalacji (deployment descriptor) – Plik XML zawierający informacje dla kontenera 141 Interfejs domowy • Interfejs Home dla klientów zdalnych (i lokalnych) – Rozszerza javax.ejb.EJBHome • Interfejs LocalHome dla klientów lokalnych – Rozszerza javax.ejb.EJBLocalHome • Zawiera metody do: – Tworzenia obiektów encyjnych: 0 lub więcej metod create() • • Typowo dodające nowy wiersz do tabeli Zwracające interfejs komponentowy – Wyszukiwania obiektów encyjnych : metody findXXX() • • Zwracające interfejs komponentowy lub Collection Obowiązkowo wśród nich findByPrimaryKey() – Usuwania obiektów encyjnych : remove() • • Typowo usuwające wiersz z tabeli Podstawowa wersja jako parametr przyjmuje klucz główny – Opcjonalnie inne metody nieodnoszące się do konkretnej instancji 142 Interfejs komponentowy • Interfejs Remote dla klientów zdalnych (i lokalnych) – Rozszerza javax.ejb.EJBObject • Interfejs Local dla klientów lokalnych – Rozszerza javax.ejb.EJBLocalObject • • • Zawiera metody getXXX/setXXX odczytujące i modyfikujące poszczególne pola Opcjonalnie może zawierać metody biznesowe Dziedziczy metody: – getEJBHome() lub getEJBLocalHome() – getPrimaryKey() 143 Klasa klucza głównego • Klucz główny – Jednoznacznie identyfikuje instancje komponentu encyjnego – Wykorzystywany przy usuwaniu instancji – Podstawowe kryterium selekcji przy wyszukiwaniu instancji • Klasa klucza głównego – Musi implementować java.io.serializable – Definiowana przede wszystkim przy kluczach złożonych • Dla kluczy prostych (pojedynczy atrybut) można wykorzystać jedną z klas bibliotecznych np. – – – – java.lang.String java.lang.Integer java.lang.Long ... 144 Klasa komponentu • • Musi implementować interfejs javax.ejb.EntityBean Dla każdej metody create() interfejsu domowego zawiera: – ejbCreate() – inicjalizującą trwałe pola (persistent fields) – ejbPostCreate() – inicjalizującą pola związków między komp. EJB • Implementuje metody: – Wyszukujące poprzez ejbFindXXX() – Inne metody interfejsu domowego poprzez ejbHomeXXX() – Metody callback z interfejsu javax.ejb.EntityBean: • ejbLoad(), ejbStore(), ejbActivate(), ejbPassivate(), ejbRemove(), setEntityContext(...), unSetEntityContext() – Metody biznesowe zawarte w interfejsie komponentowym – Opcjonalnie inne prywatne metody 145 Cykl życia encyjnego EJB Brak newInstance() setEntityContext() unsetEntityContext() Pasywny (w puli) ejbCreate() ejbPostCreate() ejbRemove() ejbActivate() ejbLoad() Gotowy ejbPassivate() ejbStore() 146 Tworzenie encyjnego komponentu EJB CMP - Przykład (1/8) • Tworzenie komponentów encyjnych CMP w oparciu o tabele 147 Tworzenie encyjnego komponentu EJB CMP - Przykład (2/8) • Kreator: wybór tabel z bazy danych Planowane wykorzystanie z serwletu, więc interfejs Local 148 Tworzenie encyjnego komponentu EJB CMP - Przykład (3/8) • Utworzone pliki dla komponentu encyjnego Emp: – – – – – JDev 9i EmpLocalHome.java – interfejs LocalHome EmpLocal.java – interfejs Local EmpBean.java – klasa komponentu ejb-jar.xml – standardowy deskryptor instalacji orion-ejb-jar.xml – specyficzny dla OC4J deskryptor instalacji JDev 10g 149 Tworzenie encyjnego komponentu EJB CMP - Przykład (4/8) • Interfejs LocalHome (EmpLocalHome.java) package mypackage1; import javax.ejb.EJBLocalHome; import javax.ejb.CreateException; import javax.ejb.FinderException; import java.util.Collection; public interface EmpLocalHome extends EJBLocalHome { EmpLocal create() throws CreateException; EmpLocal create(Long empno) throws CreateException; EmpLocal findByPrimaryKey(Long primaryKey) throws FinderException; Collection findAll() throws FinderException; } 150 Tworzenie encyjnego komponentu EJB CMP - Przykład (5/8) • Interfejs Local (EmpLocal.java) package mypackage1; import javax.ejb.EJBLocalObject; public interface EmpLocal extends EJBLocalObject { Long getEmpno(); void setEmpno(Long newEmpno); String getEname(); void setEname(String newEname); ... Long getSal(); void setSal(Long newSal); Long getDeptno(); void setDeptno(Long newDeptno); } Metody getXXX i setXXX dla pól komponentu 151 Tworzenie encyjnego komponentu EJB CMP - Przykład (6/8) • Klasa komponentu (EmpBean.java) package mypackage1.impl; import javax.ejb.EntityBean; import javax.ejb.EntityContext; import java.sql.Timestamp; ... public abstract Long getEmpno(); public abstract void setEmpno( Long newEmpno); public abstract class EmpBean implements EntityBean { private EntityContext context; public abstract String getEname(); public abstract void setEname( String newEname); public abstract Long getSal(); public abstract void setSal( Long newSal); public Long ejbCreate() { return null; } public void ejbPostCreate() {} public Long ejbCreate(Long empno) { setEmpno(empno); return empno; } public public public public public public void void void void void void ejbPostCreate(Long empno) ejbActivate() {} ejbLoad() {} ejbPassivate() {} ejbRemove() {} ejbStore() {} {} } public void setEntityContext(EntityContext ctx) { this.context = ctx; } public void unsetEntityContext() { this.context = null; } public abstract Long getDeptno(); public abstract void setDeptno( Long newDeptno); ... Metody z interfejsów Local i LocalHome CMP – setXXX/getXXX abstrakcyjne CMP – wiele spośród "callback" pustych CMP – brak metod ejbFindXXX 152 Tworzenie encyjnego komponentu EJB CMP - Przykład (7/8) • Standardowy deskryptor instalacji (ejb-jar.xml) <?xml version = '1.0' encoding = 'windows-1250'?> <!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN" "http://java.sun.com/dtd/ejb-jar_2_0.dtd"> <ejb-jar> <enterprise-beans> <entity> <description>Entity Bean ( CMP )</description> <display-name>Emp</display-name> <ejb-name>Emp</ejb-name> <local-home>mypackage1.EmpLocalHome</local-home> <local>mypackage1.EmpLocal</local> Składniki komponentu <ejb-class>mypackage1.impl.EmpBean</ejb-class> <persistence-type>Container</persistence-type> <prim-key-class>java.lang.Long</prim-key-class> <reentrant>False</reentrant> <cmp-version>2.x</cmp-version> <abstract-schema-name>Emp</abstract-schema-name> <cmp-field> <field-name>empno</field-name> </cmp-field> <cmp-field> <field-name>ename</field-name> </cmp-field> Abstrakcyjny schemat ... <cmp-field> <field-name>sal</field-name> </cmp-field> <cmp-field> <field-name>deptno</field-name> </cmp-field> <primkey-field>empno</primkey-field> </entity> </enterprise-beans> </ejb-jar> 153 Tworzenie encyjnego komponentu EJB CMP - Przykład (8/8) • Specyficzny dla OC4J deskryptor instalacji (orion-ejb-jar.xml) <?xml version = '1.0' encoding = 'windows-1250'?> <!DOCTYPE orion-ejb-jar PUBLIC "-//Evermind//DTD Enterprise JavaBeans 1.1 runtime//EN" "http://xmlns.oracle.com/ias/dtds/orion-ejb-jar.dtd"> <orion-ejb-jar> <enterprise-beans> <entity-deployment name="Emp" data-source="jdbc/marek8iDS" table="EMP"> <primkey-mapping> <cmp-field-mapping name="empno" persistence-name="EMPNO" persistence-type="NUMBER(4)"/> </primkey-mapping> <cmp-field-mapping name="empno" persistence-name="EMPNO" persistence-type="NUMBER(4)"/> <cmp-field-mapping name="ename" persistence-name="ENAME" persistence-type="VARCHAR2(10)"/> ... <cmp-field-mapping name="sal" persistence-name="SAL" persistence-type="NUMBER(7,2)"/> <cmp-field-mapping name="deptno" persistence-name="DEPTNO" persistence-type="NUMBER(2)"/> </entity-deployment> </enterprise-beans> <assembly-descriptor> <default-method-access> <security-role-mapping impliesAll="true" name="<default-ejb-caller-role>"/> </default-method-access> </assembly-descriptor> </orion-ejb-jar> Mapowanie abstrakcyjnego schematu na schemat konkretnej bazy danych 154 Korzystanie z encyjnych EJB (1/3) • Definicja referencji w deskryptorze instalacji: – web.xml – dla odwołań z serwletów i JSP – ejb-jar.xml – dla odwołań z innych EJB web.xml <web-app> ... <ejb-local-ref> <ejb-ref-name> ejb/Emp </ejb-ref-name> <ejb-ref-type> Entity </ejb-ref-type> <local-home> mypackage1.EmpLocalHome </local-home> <local> mypackage1.EmpLocal </local> </ejb-local-ref> </web-app> 155 Korzystanie z encyjnych EJB (2/3) • Wyszukiwanie metodą findByPrimaryKey() try { Context context = new InitialContext(); EmpLocalHome lh = (EmpLocalHome) context.lookup("java:comp/env/ejb/Emp"); EmpLocal el = lh.findByPrimaryKey(new Long("7499")); out.println("7499: " + el.getEname()); } catch (Exception e) { out.println(e); } • Wyszukiwanie metodą findAll() try { Context context = new InitialContext(); EmpLocalHome lh = (EmpLocalHome) context.lookup("java:comp/env/ejb/Emp"); out.println("Employees: "); Collection emps = lh.findAll(); Iterator it = emps.iterator(); while (it.hasNext()) { el = (EmpLocal) it.next(); out.println(el.getEmpno() + ": " + el.getEname() + ", " + el.getSal()); } } catch (Exception e) { out.println(e); } 7499: ALLEN Employees: 7369: SMITH, 800 7499: ALLEN, 1600 7521: WARD, 1250 7566: JONES, 2975 7654: MARTIN, 1250 7698: BLAKE, 2850 7782: CLARK, 2450 7788: SCOTT, 3000 7839: KING, 5000 7844: TURNER, 1500 7876: ADAMS, 1100 7900: JAMES, 950 7902: FORD, 3000 7934: MILLER, 1300 156 Korzystanie z encyjnych EJB (3/3) • Tworzenie, modyfikacja i usuwanie danych try { Context context = new InitialContext(); EmpLocalHome lh = (EmpLocalHome) context.lookup("java:comp/env/ejb/Emp"); // dodanie nowego pracownika EmpLocal el = lh.create(new Long("9999")); // modyfikacja danych pracownika el.setEname("BUSH"); el.setSal(new Long("4500")); // usuniecie pracownika lh.remove(new Long("9999")); } catch (Exception e) { out.println(e); } Wykorzystanie metody create utworzonej przez kreator (lepszym rozwiązaniem jest dodanie nowej metody create z "pełnym" zestawem parametrów) 157 Metody wyszukujące findXXX() • Do wyszukiwania instancji encyjnych EJB służą metody findXXX() zawarte w interfejsie Home / LocalHome – Zwracają interfejs komponentowy lub typ Collection • • BMT: twórca komponentu implementuje odpowiednie metody ejbFindXXX() w klasie komponentu CMT: dla metod findXXX() zadeklarowanych w interfejsie Home / LocalHome, deskryptor instalacji zawiera odpowiednie zapytania w języku EJB QL – Nie dotyczy findByPrimaryKey() i findAll(), które są automatycznie implementowane przez kontener CMP: Dodatkowe metody findXXX() – Przykład (1/2) 158 ejb-jar.xml • Dodanie metody wyszukującej wg pensji </ejb-jar> </enterprise-beans> </entity> ... <query> <query-method> <method-name> findByMinSal </method-name> <method-params> <method-param> int </method-param> </method-params> </query-method> <ejb-ql> SELECT DISTINCT OBJECT(e) FROM Emp e WHERE e.sal > ?1 </ejb-ql> </query> </entity> </enterprise-beans> </ejb-jar> Zapytanie odwołujące się do abstrakcyjnego schematu zdefiniowanego w deskryptorze instalacji, wyrażone w języku EJB QL CMP: Dodatkowe metody findXXX() – Przykład (2/2) • Wyszukiwanie metodą findByMinSal() try { Context context = new InitialContext(); EmpLocalHome lh = (EmpLocalHome) context.lookup("java:comp/env/ejb/Emp"); out.println("Rich Employees: "); Collection emps = lh.findByMinSal(1280); Iterator it = emps.iterator(); while (it.hasNext()) { el = (EmpLocal) it.next(); out.println(el.getEmpno() + ": " + el.getEname() + ", " + el.getSal()); } } catch (Exception e) { out.println(e); } 159 Rich Employees: 7499: ALLEN, 1600 7566: JONES, 2975 7698: BLAKE, 2850 7782: CLARK, 2450 7788: SCOTT, 3000 7839: KING, 5000 7844: TURNER, 1500 7902: FORD, 3000 7934: MILLER, 1300 160 Język EJB QL • • • Język zapytań dla EJB oparty o SQL-92 Wykorzystywany do deklaratywnego specyfikowania działania metod wyszukujących w komponentach encyjnych CMP Zapewnia przenaszalność zapytań – Zapytania definiowane w kontekście abstrakcyjnego schematu (definiowanego w deskryptorze instalacji) – Podczas instalacji zapytania tłumaczone na język danej platformy • Składnia SELECT ... FROM ... WHERE ... – Brak klauzul ORDER BY, GROUP BY, HAVING (!) SELECT DISTINCT OBJECT(e) Eliminacja duplikatów Zwracanie obiektów FROM Emp e Nazwa z abstrakcyjnego schematu WHERE e.sal > ?1 Wyrażenie ścieżkowe Pierwszy parametr 161 Powiązania między encyjnymi EJB • Zazwyczaj w modelu danych występują związki między danymi – W bazie danych istnieją związki między tabelami (poprzez klucze obce) – W aplikacji J2EE związki te mogą być reprezentowane jako powiązania między encyjnymi komponentami EJB • Podstawowe własności związku to: – Liczebność – liczba instancji biorących udział w związku: 1:1, 1:N, M:N – Kierunkowość – kierunki w których możliwa jest nawigacja wg związku: związki jedno- i dwukierunkowe • Dla komponentów CMP kontener zarządza związkami – CMRs: Container-Managed Relationships (od EJB 2.0) • Związki wykorzystują lokalne interfejsy docelowych EJB 162 Tworzenie związków • Uwagi o implementacji związków: – Dostępne tylko dla EJB 2.0 – Dla komponentów opisanych w tym samym deskryptorze instalacji • Definicja związku obejmuje: – W klasie komponentu: abstrakcyjne metody getXXX/setXXX dla każdego pola reprezentującego powiązanie – W deskryptorze instalacji: • • • • Nazwa związku, liczebność i kierunkowość Dla każdego kierunku informacje o polu reprezentującym związek Opcjonalnie dla jednego kierunku specyfikacja kaskadowego usuwania Tworzenie związków za pomocą JDevelopera – "Ręczna" edycja modułu EJB – Wykorzystanie kreatora tworzącego encyjne EJB na podstawie tabel 163 Tworzenie związku CMR - Przykład (1/2) • Nowy komponent encyjny Dept (CMP), powiązany z wcześniej utworzonym Emp (CMP) Utworzenie związków z wcześniej utworzonymi encyjnymi EJB w oparciu o klucze obce z bazy danych 164 Tworzenie związku CMR - Przykład (2/2) • Właściwości utworzonego związku 165 CMR: Metody do obsługi związku • Metody utworzone przez kreator do obsługi związku: – Interfejs EmpLocal komponentu Emp: • DeptLocal getDept_deptno() (zamiast Long getDeptno()) • void setDept_deptno(DeptLocal newDept_deptno) (zamiast void setDeptno(Long newDeptno)) – Interfejs DeptLocal komponentu Dept: • Collection getEmp_deptno() • void setEmp_deptno(Collection newEmp_deptno) CMR: Nawigacja w oparciu o związek Przykład 166 try { Context context = new InitialContext(); Odczyt nazwy EmpLocalHome lh = (EmpLocalHome) context.lookup("java:comp/env/ejb/Emp"); departamentu EmpLocal el = lh.findByPrimaryKey(new Long("7499")); dla obiektu Dept out.println("Employee #7499's department:"); out.println("7499: " + el.getEname() + ", " + el.getDept_deptno().getDname()); out.println("Employees in the same department as Emp #7499:"); DeptLocal dl = el.getDept_deptno(); Odczyt obiektu Dept związanego z danym obiektem Emp Collection emps = dl.getEmp_deptno(); Iterator it = emps.iterator(); while (it.hasNext()) { EmpLocal e = (EmpLocal) it.next(); if (!e.isIdentical(el)) out.println(e.getEmpno() + ": " + e.getEname()); } } catch (Exception e) { out.println(e); } Odczyt kolekcji obiektów Emp związanych z danym obiektem Dept Test czy 2 obiekty EJB są identyczne Employee #7499's department: 7499: ALLEN, SALES Employees in the same department as Emp #7499: 7521: WARD 7654: MARTIN 7698: BLAKE 7844: TURNER 7900: JAMES 167 Transakcje zarządzane przez kontener • W kodzie aplikacji nie ma instrukcji rozpoczynających i kończących transakcje • Funkcjonalność ta jest realizowana przez kontener w oparciu o informacje z pliku deployment descriptor • CMT to jedyna opcja dla encyjnych EJB (dla sesyjnych wybór między CMT lub BMT) ... <assembly-descriptor> <container-transaction> <description>no description</description> <method> <ejb-name>myEmployee</ejb-name> <method-name>*</method-name> </method> <trans-attribute>RequiresNew</trans-attribute> </container-transaction> </assembly-descriptor> </ejb-jar> ejb-jar.xml Transakcje zarządzane przez kontener – 168 atrybut transakcyjny • Znaczenie wartości atrybutu transakcyjnego: • Komponent encyjny korzystający z CMP, CMR musi działać w ramach transakcji (!) – Dozwolone wartości atrybutu transakcyjnego: Required, RequiresNew, Mandatory Transakcje zarządzane przez kontener – 169 wycofanie transakcji • Kod komponentu korzystającego CMT nie może zawierać instrukcji kończących transakcję (commit(), rollback()) • Komponent może zlecić kontenerowi wycofanie transakcji wołając metodę setRollbackOnly() na rzecz swego kontekstu EJBContext – Referencja na obiekt EJBContext jest zapisywana w jednym z pól komponentu przez metodę setEntityContext() na początku cyklu życia – Typowo metoda wywołuje setRollbackOnly() przed rzuceniem wyjątku – Samo rzucenie wyjątku nie wycofuje transakcji (!) – Metoda może sprawdzić czy transakcja została "zaznaczona do wycofania" metodą getRollbackOnly() ... context.setRollbackOnly(); ... Dostęp do encyjnych EJB z poziomu JSP przy użyciu biblioteki znaczników OJSP EJB • Biblioteka znaczników JSP dla EJB może być wykorzystana zarówno do operacji na komponentach sesyjnych jak i encyjnych – Plik opisu biblioteki: ejbtaglib.tld musi być instalowany z aplikacją – W Oracle iAS biblioteka należy do "dobrze znanych" • Znaczniki useHome, useBean, createBean zachowują swe znaczenie • Typowe dla komponentów encyjnych jest użycie znacznika iterate: – Do utworzenia kolekcji instancji encyjnych EJB i iteracji po niej 170 Dostęp do encyjnych EJB z poziomu JSP przy użyciu biblioteki znaczników OJSP EJB- Przykład Salaries.jsp <%@ page contentType="text/html;charset=windows-1250"%> <%@ taglib uri="/WEB-INF/ejbtaglib.tld" prefix="ejb" %> Atrybut local konieczny <html> przy korzystaniu <head> <title>Employees</title> </head> z interfejsów lokalnych <body> <ejb:useHome id="home" type="mypackage1.EmpLocalHome" location="java:comp/env/ejb/Emp" local="true"/> <ejb:iterate id="emp" type="mypackage1.EmpLocal" collection="<%=home.findAll()%>" max="50"> Employee #<%=emp.getEmpno()%>: <%=emp.getEname()+" "+ emp.getSal()%>$. <br> </ejb:iterate> </body> <%@ page contentType="text/html;charset=windows-1250"%> </html> <%@ taglib uri="/WEB-INF/ejbtaglib.tld" prefix="ejb" %> <html> <head> <title>Employees</title> </head> <body> <ejb:useHome id="home" type="mypackage1.EmpLocalHome" location="java:comp/env/ejb/Emp" local="true"/> <ejb:iterate id="emp" type="mypackage1.EmpLocal" collection="<%=home.findAll()%>" max="50"> Employee #<jsp:getProperty name="emp" property="empno" />: <jsp:getProperty name="emp" property="ename" /> <jsp:getProperty name="emp" property="sal" />$. <br> </ejb:iterate> </body> </html> Możliwość korzystania jak z JavaBeans 171