Typowy obiekt encyjny charakteryzuje się:
Typowy kontener i serwer zapewniają skalowalne środowisko dla wielu działających jednocześnie aktywnych obiektów encyjnych.
Zdalny klient komunikuje się z obiektem encyjnym przez zdalny interfejs i używa zdalnego interfejsu domowego. Zdalny interfejs domowy dostarcza referencję do zdalnego interfejsu obiektu encyjnego. Zdalny interfejs nie zależy od lokalizaji obiektu. Klient działający na tej samej maszynie wirtualnej na której znajduje się instancja obiektu encyjnego używa tego samego API co klient działający na innej maszynie wirtualnej czy fizycznej. Zdalny interfejs bazuje na Java RMI (Java Remote Method Invocation) API, czyli zdalnym wywoływaniu metod. Obiekty te są więc dostępne przez standardowe wywoływanie metod, czego konsekwecją jest np. to, że argumenty i wyniki przekazywane są przez wartość. Zdalnym klientem może być zarówno ejb umieszczony w tym samym lub innym kontenerze jak i dowolny program w Javie, aplet lub servlet. Istnieje także możliwość mapowania na interfejs CORBY przez co z ejb mogą także korzystać klienci nie napisani w Javie.
Context initialContext = new InitialContext();
AccountHome accountHome = (AccountHome)
javax.rmi.PortableRemoteObject.narrow(
initialContext.lookup("java:comp/env/ejb/accounts"),
AccountHome.class);
Context initialContext = new InitialContext();
AccountHome accountHome = (AccountHome)
initialContext.lookup("java:comp/env/ejb/accounts");
Zdalny interfejs domowy ziarenka encyjnego pozwala klientowi na:
javax.ejb.EJBMetaData
dla ziarenka; javax.ejb.EJBMetaData
ma pozwolić narzędziom do integrowania aplikacji wydobyć meta dane z informacją o EJB.javax.ejb.EJBHome
i podlegać standardowym zasadom programowania zdalnych interfejsów w Javie.
create
create<METHOD>(...)
,
jedną dla każdego sposobu tworzenia obiektu encyjnego. Argumenty tych metod służą zwykle inicjalizacji nowo
tworzonego obiektu. Nazwa każdej tworzonej metody zaczyna się od prefiksu "create". Każda z metod
create<METHOD>
zwraca zdalny interfejs ziarenka. W przypadku niepowodzenia może zostać
rzucony wyjątek java.rmi.RemoteException
lub javax.ejb.CreateException
.
Następujący interfejs domowy deklaruje trzy metody tworzące:
public interface AccountHome extends javax.ejb.EJBHome {
public Account create(String firstName, String lastName,
double initialBalance)
throws RemoteException, CreateException;
public Account create(String accountNumber,
double initialBalance)
throws RemoteException, CreateException,
LowInitialBalanceException;
public Account createLargeAccount(String firstname,
String lastname, double initialBalance)
throws RemoteException, CreateException;
...
}
AccountHome accountHome = ...;
Account account = accountHome.create("John", "Smith", 500.00);
find
findLargeAccounts(...)
. Argumenty metody wyszukującej używane są przez
implementację EJB do zlokalizowania szukanego obiektu. Tego typu metody zwracają wynik typu zdalnego
interfejsu EJB lub kolekcji obiektów implementujących zdalny interfejs EJB.
Zbiór rzucanych wyjątków zawiera java.rmi.RemoteException
oraz
javax.ejb.FinderException
.
findByPrimaryKey(primaryKey)
, która
pozwala klientowi zlokalizować obiekt encyjny używający danej wartości jako klucza głównego.
Nazwa tej metody jest zawsze taka sama, ma ona pojedynczy argument typu klucza głównego i zwraca
obiekt typu zdalnego interfejsu. Implementacja findByPrimaryKey(primaryKey)
musi
gwarantować, że odpowiedni obiekt encyjny istnieje.
Następujący przykład pokazuje implementację tej metody:
public interface AccountHome extends javax.ejb.EJBHome {
...
public Account findByPrimaryKey(String AccountNumber)
throws RemoteException, FinderException;
}
Oto jak klient wywołuje findByPrimaryKey
:
AccountHome = ...;
Account account = accountHome.findByPrimaryKey("100-3450-3333");
remove
javax.ejb.EJBHome
definjuje kilka metod, które umożliwiają
klientowi usuwanie obiektów encyjnych.
public interface EJBHome extends Remote {
void remove(Handle handle) throws RemoteException,
RemoveException;
void remove(Object primaryKey) throws RemoteException,
RemoveException;
}
java.rmi.NoSuchObjectException
.
home
W zbiorze rzucanych wyjątków znajduje się standardowo java.rmi.RemoteException
Oto prosty przykład dwóch metod:
public interface EmployeeHome extends javax.ejb.EJBHome {
...
// this method returns a living index depending on
// the state and the base salary of an employee:
// the method is not specific to an instance
public float livingIndex(String state, float Salary)
throws RemoteException;
// this method adds a bonus to all of the employees
// based on a company profit-sharing index
public void addBonus(float company_share_index)
throws RemoteException, ShareIndexOutOfRangeException;
...
}
Lokalny interfejs domowy pozwala klientowi na:
javax.ejb.EJBLocalHome
.
create
create<METHOD>(...)
.
Nazwa każdej z nich zaczyna się od "create". Jako wynik metody tego typu zwracają lokalny interfejs
obiektu encyjnego. Rzucane wyjątki zawierają javax.ejb.CreateException
.
Przykład użycia metod create
w lokalnym interfejsie domowym:
public interface AccountHome extends javax.ejb.EJBLocalHome {
public Account create(String firstName, String lastName,
double initialBalance)
throws CreateException;
public Account create(String accountNumber,
double initialBalance)
throws CreateException, LowInitialBalanceException;
public Account createLargeAccount(String firstname,
String lastname, double initialBalance)
throws CreateException;
...
}
Przykład wywołania po stronie klienta:
AccountHome accountHome = ...;
Account account = accountHome.create("John", "Smith", 500.00);
find
findLargeAccounts(...)
. Metody te zwracają lokalny
interfejs ziarenka lub kolekcję obiektów implementujących ten interfejs. Wśród wyjątków mamy
javax.ejb.FinderException
. Nie można natomiast rzucać java.rmi.RemoteException
.
Lokalny interfejs domowy zawiera metodę findByPrimaryKey(primaryKey)
(uwagi jak przy interfejsie
zdalnym).
Przykład findByPrimaryKey
public interface AccountHome extends javax.ejb.EJBLocalHome {
...
public Account findByPrimaryKey(String AccountNumber)
throws FinderException;
}
Po stronie klienta:
AccountHome = ...;
Account account = accountHome.findByPrimaryKey("100-3450-3333");
remove
javax.ejb.EJBLocalHome
definiuje metody pozwalające klientowi
na usuwanie obiektów encyjnych.
public interface EJBLocalHome {
void remove(Object primaryKey) throws RemoveException,
EJBException;
}
javax.ejb.NoSuchObjectLocalException
.
home
java.rmi.RemoteException
.
Np.
public interface EmployeeHome extends javax.ejb.EJBLocalHome {
...
// this method returns a living index depending on
// the state and the base salary of an employee:
// the method is not specific to an instance
public float livingIndex(String state, float Salary);
// this method adds a bonus to all of the employees
// based on a company profit sharing index
public void addBonus(float company_share_index)
throws ShareIndexOutOfRangeException;
...
}
Obiekt encyjny nie istnieje dopóki nie zostanie stworzony. Dopóki nie zostanie stworzony, dopóty nie ma torzsamości. Przez tworzenie zyskuje torzsamość. Klient tworzy obiekt encyjny przy użyciu interfejsu domowego. Po stworzeniu obiektu encyjnego klient uzyskuje do niego referencję.
W środowisku z danymi dziedziczonymi, obiekty encyjne mogą "istnieć " zanim kontener i EJB zostaną
zainicjalizowane. Na dodatek obiekt encyjny może zostać "stworzony" za pomocą mechanizmów innych
niż wywołanie metody create<METHOD>
(np. przez wstawienie rekordu do bazy) i wciąż
może być dostępny przez metody wyszukujące. Obiekt encyjny może być bezpośrednio usunięty przy pomocy
innych narzędzi niż wykonanie remove()
(np. usunięcie rekordu z bazy). Przejścia "direct insert"
oraz "direct delete" na diagramie odpowiadają bezpośrednim manipulacjom na bazie.
Wszystkie obiekty encyjne uznawane są za obiekty trwałe. Czas życia obiektu encyjnego nie jest ograniczony do czasu życia procesu wirtualnej maszyny Javy w której istnieje instancja EJB. Awaria wirtualnej maszyny Javy może spowodować wycofanie obecnej transakcji, nie powoduje natomiast zniszczenia wcześniej stworzonych obiektów ani nie unieważnia referencji do interfejsu domowego ani interfejsów klientów trzymanych przez klientów. Wielu klientów może pracować jednocześnie na tym samym obiekcie. Do izolacji używa się transakcji.
Każdy obiekt encyjny ma unikatowy identyfikator w obrębie swego interfejsu domowego. Jeśli dwa obiekty mają ten sam interfejs domowy i taki sam klucz główny, są uważane za identyczne.
Architektura Enterprise JavaBeans pozwala, aby klasa klucza głównego była dowolną klasą będącą legalnym typem dla protokołu RMI-IIOP. Klasa klucz głównego może być zależna od klasy ziarenka (tzn. klasa EJB może zdefiniować jako swój klucz główny nową klasę lub użyć klucza głównego innej klasy EJB).
Klient może dostać identyfikator obiektu wywołując metodę getPrimaryKey()
na referencji do
obiektu.
Identyfikator obiektu nie ulega zmianie (tzn. getPrimaryKey()
zawsze zwraca tą samą wartość
jeśli jest wywoływana na tym samym obiekcie). Jeśli obiekt encyjny ma zarówno interfejs zdalny jak i lokalny to
wywołanie getPrimaryKey()
zwróci ten sam wynik.
Klient może sprawdzić czy dwie referencje do obiektów encyjnych odnoszą się do tego samego obiektu przez
wywołanie metody isIdentical
. Alternatywnie, jeśli obie te referencje zostały pozyskane przez
ten sam interfejs domowy, można porównać ich klucze główne przy pomocy metody equals
.
Następujący przykład ilustruje użycie metody isIdentical
:
Account acc1 = ...;
Account acc2 = ...;
if (acc1.isIdentical(acc2)) {
acc1 and acc2 are the same entity object
} else {
acc2 and acc2 are different entity objects
}
Klient znając klucz główny obiektu encyjnego może dostać do niego referencję przez
wywołanie findByPrimaryKey(key)
na interfejsie domowym.
Architektura Enterprise JavaBeans nie określa "równości obiektów" (np. użycia operatora ==) dla
referencji do obiektów encyjnych. Wynik metody Object.equals(Object obj)
jest
nieokreślony. Dlatego klient powinien zawsze używać isIdentical
.
javax.ejb.EJBObject
. Zdalny interfejs definiuje
metody biznesowe, które mogą być wywoływane przez zdalnych klientów.
Poniższy przykład ilustruje definicję zdalnego interfejsu Entity EJB:
public interface Account extends javax.ejb.EJBObject {
void debit(double amount)
throws java.rmi.RemoteException,
InsufficientBalanceException;
void credit(double amount)
throws java.rmi.RemoteException;
double getBalance()
throws java.rmi.RemoteException;
}
Interfejs javax.ejb.EJBObject
definiuje metody, które pozwalają klientowi na
następujące operacje na referencjach do obiektów:
Kontener dostarcza implementację metod zdefiniowanych w interfejsie javax.ejb.EJBObject
.
Tylko metody biznesowe muszą być zaimplementowane w klasie ziarenka.
Obiekt encyjny nie pozwala klientom na wywoływanie metod interfejsu javax.ejb.EnterpriseBean
przez klienta. Są one używane tylko przez kontener do zarządzania instancjami ziarenek.
javax.ejb.EJBLocalObject
. Lokalny interfejs definiuje metody
biznesowe dostępne dla lokalnych klientów.
Poniższy przykład pokazuję jak może wyglądać lokalny interfejs:
public interface Account extends javax.ejb.EJBLocalObject {
void debit(double amount)
throws InsufficientBalanceException;
void credit(double amount);
double getBalance();
}
Interfejs javax.ejb.EJBLocalObject
pozwala lokalnym klientom na wykonanie
następujących operacji na referencji do lokalnego obiektu encyjnego:
Uchwyt do obiektu encyjnego to obiekt identyfikujący obiekt encyjny w sieci. Klient posiadający
referencję do zdalnego interfejsu obiektu encyjnego może uzyskać uchwyt przez wywołanie
metody getHandle()
na zdalnym interfejsie.
Ponieważ klasa uchwytu dziedziczy z java.io.Serializable
, klient może
serializować uchwyt i użyć go potem, być może w innym procesie lub nawet systemie, aby
z powrotem uzyskać referencję do obiektu encyjnego identyfikowanego przez uchwyt.
Kod klienta musi użyć metody javax.rmi.PortableRemoteObject.narrow(...)
aby przekształcić wynik metody getEJBObject()
wywołanej na typie uchwytu.
Czas życia i zakres uchwytu zależy od jego implementacji. Program działający w jednej maszynie wirtualnej Javy powinien móc uzyskać i zserializować uchwyt, a inny program działający na innej maszynie deserializować uchwyt i odtworzyć referencję do obiektu. Uchwyt jest zwykle implementowany w ten sposób, że można go używać przez długi okres czasu - minimalny czas - restart serwera.
Kontener EJB nie musi akceptować uchwytu wygenerowanego przez kontener innego dostawcy.
Użycie uchwytu ilustruje następujący przykład:
// A client obtains a handle of an account entity object and
// stores the handle in stable storage.
//
ObjectOutputStream stream = ...;
Account account = ...;
Handle handle = account.getHandle();
stream.writeObject(handle);
// A client can read the handle from stable storage, and use the
// handle to resurrect an object reference to the
// account entity object.
//
ObjectInputStream stream = ...;
Handle handle = (Handle) stream.readObject(handle);
Account account = (Account)javax.rmi.PortableRemoteObject.narrow(
handle.getEJBObject(), Account.class);
account.debit(100.00);
Uchwyt nie zawiera praw dostępu do obiektu. Po otrzymaniu z uchwytu referencji do obiektu, przy próbie wywołania metody na tym obiekcie, kontener dokonuje standardowej kontroli praw dostępu bazujących na prawach wywołującego.
Specyfikacja EJB pozwala klientowi na uzyskanie uchwytu do zdalnego interfejsu domowego. Klient może zapamiętać ten uchwyt w pamięci stałej i odtworzyć później referencję. Funkcjonalność uchwytu może być użyteczna dla klienta, który potrzebuje użyć interfejsu zdalnego w przyszłości, ale nie zna jego nazwy JNDI.
Uchwyt do zdalnego interfejsu domowego musi implementować interfejs ejb.HomeHandle
.
W kodzie klienta używa się metody javax.rmi.PortableRemoteObject.narrow(...)
aby przekształcić wynik getEJBHome()
- metody uchwytu na interfejs domowy.
Czas życia i zasięg - jak w uchwycie obiektu encyjnego.
Program kliencki, który ma być przenośny między różnymi implementacjami kontenerów EJB
musi używać javax.rmi.PortableRemoteObject.narrow(...)
do zawężania typów
zdalnego interfejsu i zdalnego interfejsu domowego
Programy, które używają operatora rzutowania w celu zawężania interfejsu zdalnego i zdalnego interfejsu domowego prawdopodobnie zawiodą jeśli implementacja kontenera używa RMI-IIOP do komunikacji.
cmp-field
oraz cmr-field
.
Nazwy tych pól muszą być legalnymi identyfikatorami Javy i muszą zaczynać się od małej literyget
, set
). Implementacją
tych metod zajmuje się kontenerjava.util.Collection
lub java.util.Set
. To, która z tych kolekcji
zostanie użyta określa się w deskryptorzecmr-field
, ale
nie może być typem pola cmp-field
set
cmp-fields
były podstawowymi typami Javy oraz
typami serializowalnymiZiarenko EJB może być w relacji z innymi ziarenkami zarządzanymi przez kontener.
Relacje mogą być typu jeden do jeden, jeden do wiele lub wiele do wiele.
Relacje zarządzane przez kontener mogą istnieć tylko miedzy ziarenkami w ramach tego samego środowiska lokalnego definiowanego w sekcji dotyczącej relacji w deskryptorze wdrożeniowym.
Relacje mogą być dwukierunkowe lub jednokierunkowe. Jeśli relacja jest dwukierunkowa możliwe są przejścia między obiektami w obie strony. Jeśli jednokierunkowa - tylko w jedną stronę. Kierunek relacji określa się również w deskryptorze. Ziarenko, które nie ma interfejsu lokalnego może mieć tylko jednokierunkowe połączenia do innych ziarenek. Brak lokalnego interfejsu uniemożliwia włączanie do relacji przez inne ziarenka.
Programista EJB musi zdecydować o liczności relacji w momencie tworzenia ziarenka. Metody get dla pól
cmr-field muszą zwracać albo lokalny interfejs, albo kolekcję (java.util.Collection
lub
java.util.Set
) lokalnych interfejsow. Analogicznie dla parametrów metod set.
remove
interfejsu ziarenka.cascade-delete
(umieszczane w deskryptorze).
W wyniku wywołania metody remove
na obiekcie encyjnym, kontener musi wywołać
ejbRemove()
. Po zakończeniu ejbRemove()
kontener musi usunąć obiekt
encyjny ze wszystkich relacji do których należał, a potem usunąć jego trwałe dane. Po usunięci akcesory
relacji jeden do jeden z tym obiektem zwrócą null; w przypadku relacji wiele do wiele wynikiem będzie
kolekcja bez tego obiektu. Kontener musi wykrywać odwołania do usuniętego obiektu i rzucać
wyjątek java.rmi.NoSuchObjectException
(zdalny klient) lub
javax.ejb.NoSuchObjectLocalException
(lokalny klient).
Element cascade-delete
deskryptora wdrożeniowego może być zawarty jest w
elemencie ejb-relation-ship-role
wewnątrz ejb-relation
tylko
jeśli drugi element ejb-relationship-role
wewnątrz tego samego
ejb-relation
deklaruje liczność jeden.
Cascade-delete
powoduje przy usuwaniu obiektu encyjnego usunięcie również
obiektów połączonych z nim relacją.
Poniższy przykład pokazuje jak może wygłądać kod ziarenka Entity EJB oraz deskryptor wdrożeniowy.
package com.acme.order;
// This example shows the implementation of OrderBean, the
// entity bean class for the OrderEJB entity bean. OrderEJB has
// container-managed relationships with the entity beans
// CustomerEJB and LineItemEJB.
// This example illustrates the use of local interfaces.
import java.util.Collection;
import java.util.Vector;
import java.util.Date;
import javax.naming.*;
public abstract class OrderBean implements javax.ejb.EntityBean {
private javax.ejb.EntityContext context;
// define status codes for processing
static final int BACKORDER = 1;
static final int SHIPPED = 2;
static final int UNSHIPPED = 3;
// get and set methods for the cmp fields
public abstract int getOrderStatus();
public abstract void setOrderStatus(int orderStatus);
public abstract boolean getCreditApproved();
public abstract void setCreditApproved(boolean creditapproved);
public abstract Date getOrderDate();
public abstract void setOrderDate(Date orderDate);
// get and set methods for the relationship fields
public abstract Collection getLineItems();
public abstract void setLineItems(Collection lineitems);
public abstract Customer getCustomer();
public abstract void setCustomer(Customer customer);
// business methods.
// addLineItem:
// This method is used to add a line item.
// It creates the lineitem object and adds it to the
// persistent managed relationship.
public void addLineItem(Product product, int quantity, Address address)
throws InsufficientInfoException
{
// create a new line item
if (validAddress(address)) {
// Address is a legacy class. It is a dependent value
// class that is available both in the client and in
// the entity bean, and is serializable.
// We will use the address as the value of a cmp field
// of lineItem.
try {
Context ic = new InitialContext();
LineItemLocalHome litemLocalHome =
(LineItemLocalHome)ic.lookup("LineItemEJB");
LineItem litem = litemLocalHome.create();
litem.setProduct(product);
litem.setQuantity(quantity);
litem.setTax(calculateTax(product.getPrice(), quantity, address));
litem.setStatus(UNSHIPPED);
// set the address for the line item to be shipped
litem.setAddress(address);
// The lineItem entity bean uses a dependent value
// class to represent the dates for the order status.
// This class holds shipment date, expected shipment
// date, credit approval date, and inventory
// dates which are internal to the order fullfillment
// process. Not all this information will be available
// to the client.
Dates dates = new Dates();
litem.setDates(dates);
getLineItems().add(litem);
} catch (Exception someexception) {}
} else {
throw new InsufficientInfoException();
}
}
// getOrderLineItems:
// This method makes a view of the lineitems that are in this
// order available in the client. It makes only the relevant
// information visible to the client and hides the internal
// details of the representation of the lineitem
public Collection getOrderLineItems() {
Vector clientlineitems = new Vector();
Collection lineitems = getLineItems();
java.util.Iterator iterator = lineitems.iterator();
// ClientLineItem is a value class that is used in
// the client view.
// The entity bean provider abstracts from the persistent
// representation of the line item to construct the client
// view.
ClientLineItem clitem;
while (iterator.hasNext()) {
LineItem litem = (LineItem)iterator.next();
clitem = new ClientLineItem();
// only the name of the product is available in the
// client view
clitem.setProductName(litem.getProduct().getName());
clitem.setQuantity(litem.getQuantity());
// the client view gets a specific descriptive message
// depending on the line item status.
clitem.setCurrentStatus(
statusCodeToString(litem.getStatus()));
// address is not copied to the client view.
// as this class includes other information with
// respect to the order handing that should not be
// available to the client. Only the relevant info
// is copied.
int lineitemStatus = litem.getStatus();
if ( lineitemStatus == BACKORDER) {
clitem.setShipDate(
litem.getDates().getExpectedShipDate());
} else
if (lineitemStatus == SHIPPED) {
clitem.setShipDate(
litem.getDates().getShippedDate());
}
//add the new line item
clientlineitems.add(clitem);
}
// return the value objects to the client
return clientlineitems;
}
// other methods internal to the entity bean class
...
// other javax.ejb.EntityBean methods
...
}
Deskryptor wdrożeniowy dostarcza następujących informacji o obiektach trwałych oraz relacjach:
ejb-name
obowiązkowy dla każdego ziarenka. ejb-name
musi być poprawną nazwą javową i musi być unikalny w obrębie pliku ejb-jar
.abstract-schema-name
. Musi być poprawną nazwą. Wykorzystywany
w zapytaniach EJB QL.ejb-relation
, z których każdy zawiera parę elementów
ejb-relationship-role
do opisu obu stron relacji.ejb-relationship-role
opisuje jedną stronę relacji:
jej nazwę, liczność, kierunek. Określa również nazwę pola cmr-field
określającego nazwę pod jaką programista widzi relację. Jeśli typem tego pola jest
java.util.Collection
lub java.util.Set
to musi on być wyspecyfikowany.
Nazwa EJB którego dotyczy relacja zawarta jest w elemencie ejb-name
zawartym
w relationship-role-source
.
<ejb-jar>
...
<enterprise-beans>
...
</enterprise-beans>
<relationships>
<!--
ONE-TO-MANY: Order LineItem
-->
<ejb-relation>
<ejb-relation-name>Order-LineItem</ejb-relation-name>
<ejb-relationship-role>
<ejb-relationship-role-name>
order-has-lineitems
</ejb-relationship-role-name>
<multiplicity>One</multiplicity>
<relationship-role-source>
<ejb-name>OrderEJB</ejb-name>
</relationship-role-source>
<cmr-field>
<cmr-field-name>lineItems</cmr-field-name>
<cmr-field-type>java.util.Collection
</cmr-field-type>
</cmr-field>
</ejb-relationship-role>
<ejb-relationship-role>
<ejb-relationship-role-name>lineitem-belongsto-order
</ejb-relationship-role-name>
<multiplicity>Many</multiplicity>
<cascade-delete/>
<relationship-role-source>
<ejb-name>LineItemEJB</ejb-name>
</relationship-role-source>
<cmr-field>
<cmr-field-name>order</cmr-field-name>
</cmr-field>
</ejb-relationship-role>
</ejb-relation>
<!--
ONE-TO-MANY unidirectional relationship:
Product is not aware of its relationship with LineItem
-->
<ejb-relation>
<ejb-relation-name>Product-LineItem</ejb-relation-name>
<ejb-relationship-role>
<ejb-relationship-role-name>
product-has-lineitems
</ejb-relationship-role-name>
<multiplicity>One</multiplicity>
<relationship-role-source>
<ejb-name>ProductEJB</ejb-name>
</relationship-role-source>
<!--
since Product does not know about LineItem
there is no cmr field in Product for accessing
Lineitem
-->
</ejb-relationship-role>
<ejb-relationship-role>
<ejb-relationship-role-name>
lineitem-for-product
</ejb-relationship-role-name>
<multiplicity>Many</multiplicity>
<relationship-role-source>
<ejb-name>LineItemEJB</ejb-name>
</relationship-role-source>
<cmr-field>
<cmr-field-name>product</cmr-field-name>
</cmr-field>
</ejb-relationship-role>
 </ejb-relation>
<!--
ONE-TO-MANY: Order Customer:
-->
<ejb-relation>
<ejb-relation-name>Order-Customer</ejb-relation-name>
<ejb-relationship-role>
<ejb-relationship-role-name>
customer-has-orders
</ejb-relationship-role-name>
<multiplicity>One</multiplicity>
<relationship-role-source>
<ejb-name>CustomerEJB</ejb-name>
</relationship-role-source>
<cmr-field>
<cmr-field-name>orders</cmr-field-name>
<cmr-field-type>java.util.Collection
</cmr-field-type>
</cmr-field>
</ejb-relationship-role>
<ejb-relationship-role>
<ejb-relationship-role-name>
order-belongsto-customer
</ejb-relationship-role-name>
<multiplicity>Many</multiplicity>
<relationship-role-source>
<ejb-name>OrderEJB</ejb-name>
</relationship-role-source>
<cmr-field>
<cmr-field-name>customer</cmr-field-name>
</cmr-field>
</ejb-relationship-role>
</ejb-relation>
</relationships>
...
</ejb-jar>
Instancja ziarenka może być w jednym z następujących stanów:
Programista EJB odpowiada za implementację następujących metod w abstrakcyjnej klasie ziarenka:
public void setEntityContext(EntityContext ic);
EntityContext
do ziarenka. Jeśli ziarenko zamierza wykorzystywać interfejs EntityContext
to
musi zapamiętać tą referencję.public void unsetEntityContext();
public PrimaryKeyClass ejbCreate<METHOD>(...);
ejbCreate<METHOD>(...)
, których sygnatury
odpowiadają sygnaturą metod create<METHOD>(...)
interfejsu domowego.
Kontener wywołuje metodę ejbCreate<METHOD>(...) na instancji ziarenka gdy klient
wywoła odpowiadającą metode na interfejsie domowym. Obowiązkiem programisty jest inicjalizacja
instancji (przez odpowiednie metody get i set) wartościami przekazanymi jako parametry metody.
Nie wolno tu modyfikować pól cmr-field (służy do tego metoda ejbPostCreate<METHOD>(...)
.
Tworzony obiekt encyjny musi mieć unikatowy (w obrębie danego obiektu domowego) klucz główny.
ejbCreate<METHOD>(...)
powinna być tak zaimplementowana żeby zwracać null.public void ejbPostCreate<METHOD>(...);
ejbCreate<METHOD>(...)
istnieje odpowiadająca jej metoda
ejbPostCreate<METHOD>(...)
, która ma te same parametry wejściowe.
Programista może w tej metodzie ustawić wartości pól cmr-field.public void ejbActivate();
getPrimaryKey()
, getEJBLocalObject()
,
orgetEJBObject()
na interfejsie EntityContext
.
public void ejbPassivate();
ejbActivate()
zasoby.
Nie można korzystać z akcesorów w celu uzyskania dostępu do pól persystentnych.
Można wywoływać getPrimaryKey()
, getEJBLocalObject()
,
orgetEJBObject()
na interfejsie EntityContext
.
public void ejbRemove();
remove
interfejsu domowego albo w wyniku cascade-delete. Można tu wykonać pewne operacje konieczne
przed usunięciem trwałych danych. Stan ziarenka po zakończeniu tej metody powinien odpowiadać
stanowi ziarenka po operacji ejbPassivate()
, gdyż w obu przypadkach ziarenko
trafia do puli rezerwowej.
public void ejbLoad();
ejbLoad()
. Generalnie każda wartość zależąca od danych trwałych powinna
zostać ponownie przeliczona.public void ejbStore();
ejbStore()
na instancji. Powinno się w tym momencie
odświerzyć stan danych trwałych ziarenka.public primary key type or collection ejbFind<METHOD>(...);
public type ejbHome<METHOD>(...);
<METHOD>(...)
interfejsu domowego. Instancja jest wtedy w stanie
rezerwowym i wraca do tego stanu po zakończeniu działania metody.public abstract type ejbSelect<METHOD>(...);
select
.
Metody te nie są bezpośrednio dostępne dla klienta. Są one zwykle wywoływane wewnątrz
metod biznesowych i są zdefiniowane jako metody abstrakcyjne. Implementacja jest
generowana podczas wdrożenia przez narzędzia producenta kontenera.EJB QL to język zapytań przeznaczony dla ziarenek z trwałością zarządzaną przez kontener. EJB QL może zostać skompilowany do docelowego języka zapytań bazy danych takiego jak SQL. Umożliwia szeroko pojętą przenośność oraz optymalizację zapytań podczas tłumaczenia na język docelowy (a nie w czasie wykonania).
Zapytania EJB QL mogą być wykorzystywane na dwa różne sposoby:
find
zdefiniowane w interfejsie
domowym. Metody te pozwalają klientowi wykorzystać zwracane przez nie dane. Wartościa zwracaną
przez metodę find
jest obiekt encyjny lub lokalny obiekt encyjny (lub ich kolekcję).select
dziedziczonych z abstrakcyjnej klasy
ziarenka. Zwracany wynik może być wykorzystany wewnątrz kodu ziarenka, ale metody nie
są widziane przez klienta. Metody select
zwracają EJBLocalObject
(lub
kolekcję EJBLocalObjects
), EJBObject
(lub kolekcję EJBObjects
),
albo też pole cmp-field
(lub ich kolekcję).Zapytanie EJB QL to napis składający się z trzech następujących części:
W zapytaniach EJB QL możemy się odnosić do trwałych pól innych ziarenek. Odnosimy
się do nich przez abstakcyjne nazwy (element <abstract-schema-name>
).
Przyjmujemy następujące konwencje nazewnicze:
Ziarenko encyjne jako całość ma nazwę <name>EJB
, a jego klasa i abstrakcyjna
nazwa to <name>
. W pierwszym przykładzie zakładamy, że programista
implementuje ziarenka OrderEJB
, Pro-ductEJB
,
LineItemEJB
, ShippingAddressEJB
oraz BillingAddressEJB
.
Są one zawarte w tym samym pliku ejb-jar. Tylko dwa ziarenka: OrderEJB
oraz
ProductEJB
mają interfejs zdalny. Zależności pokazano na poniższym rysunku.
Zapytanie EJB QL znajdujące wszystkie zamówienia z podległymi pozycjami może być zapisane następująco:
SELECT DISTINCT OBJECT(o)
FROM Order AS o, IN(o.lineItems) AS l
WHERE l.shipped = FALSE
Zapytanie znajdujące zamównienia dla produktów typu office supplies:
SELECT DISTINCT OBJECT(o)
FROM Order o, IN(o.lineItems) l
WHERE l.product.product_type = office_supplies
Znajdź zamówienia zawierające jakieś produkty:
SELECT DISTINCT OBJECT(o)
FROM Order o, IN(o.lineItems) l
SELECT OBJECT(o)
FROM Order o
WHERE o.lineItems IS NOT EMPTY
select
Następujące zapytania zwracają wartości inne niż ziarenka ejb.
Następujące zapytania EJB QL wybiera nazwy wszystkich zamówionych produktów.
SELECT DISTINCT l.product.name
FROM Order o, IN(o.lineItems) l
Następujące zapytanie odnajduje nazwy produktów w zamówieniu o określonym numerze. Numer zamówienia podany jest do zapytania jako parametr.
SELECT l.product.name
FROM Order o, IN(o.lineItems) l
WHERE o.ordernumber = ?1
W przypadku ziarenek z trwałością zarządzaną przez ziarenko, programista ma możliwość
ręcznie zaimplementować metody odpowiadające za utrwalanie i odczytywanie trwałych danych.
Używając trwałości zarządzanej przez kontener, programista łączy się z bazą (np. przez JDBC
lub SQLJ) bezpośrednio w kodzie ziarenka. Zapytania do bazy umieszczane są w metodach
ejbCreate<METHOD>(...)
, ejbRemove()
,
ejbFind<METHOD>(...)
, ejbLoad()
i
ejbStore()
oraz/lub w metodach biznesowych.
W przeciwieństwie do trwałości zarządzanej przez kontener, programista nie opisuje relacji w deskryptorze wdrożeniowym. Musi sam zadbać o zachowanie odpowiednich zależności wewnątrz kodu ziarenka.
Instancja ziarenka może być w jednym z następujących stanów:
Programista EJB odpowiada za implementację następujących metod w abstrakcyjnej klasie ziarenka (opisuję tylko metody różniące się od odpowiednich metod w ziarenku z trwałością zarządzaną przez kontener):
public PrimaryKeyClass ejbCreate<METHOD>(...);
ejbCreate<METHOD>(...)
, których
sygnatury odpowiadają sygnaturom metod create<METHOD>(...)
interfejsu
domowego. Kontener wywołuje ejbCreate<METHOD>(...)
na instancji ziarenka
zawsze, gdy klient wywołuje odpowiednią create<METHOD>(...)
w celu
stworzenia nowego obiektu encyjnego. Typowa implementacja ejbCreate<METHOD>(...)
sprawdza poprawność argumentów i wstawia rekordy reprezentujące obiekt encyjny do bazy
danych. Metoda inicjalizuje także zmienne ziarenka. Metoda ejbCreate<METHOD>(...)
musi zwrócić klucz główny nowo stworzonego obiektu encyjnego.
public void ejbPostCreate<METHOD>(...);
Dla każdej metody ejbCreate<METHOD>(...)
istnieje odpowiadająca
metoda ejbPostCreate<METHOD>(...)
mająca te same argumenty.
Jest ona wywoływana przez kontener po zakończeniu ejbCreate<METHOD>(...)
.
Instancja może np. pozyskać interfejs powiązanego ziarenka i przekazać go jako argument
metody do innego ziarenka.
public void ejbActivate();
ejbLoad()
.
public void ejbPassivate();
ejbStore()
.
public void ejbRemove();
remove
. Instancja ziarenka powinna użyć tej metody do usunięcia
reprezentacji obiektu z bazy.
public void ejbLoad();
public void ejbStore();
public primary key type or collection ejbFind<METHOD>(...);
Wywoływana przez kontener w odpowiedzi na wywołanie przez klienta metody
find<METHOD>(...)
. Ziarenko może wykorzystać parametry w
zapytaniu do bazy znajdującym żądany obiekt lub kolekcję obiektów.