Introduzione
Con l'avvento della specifica EJB 2.0 sono state introdotte
alcune radicali modifiche relativamente agli entità
beans di tipo CMP.
Anche se gli application server dovranno ancora supportare
entrambe le specifiche CMP (1.1 e 2.0), i due sistemi
sono incompatibili fra loro tanto che il programmatore
dovrà scegliere quale dei due adottare.
Le innovazioni riguardano tre aspetti principali:
-
la possibilità di definire lo schema di persistenza
del bean in modo astratto, più potente ed a
granularità più fine
- la
gestione automatica dei legami relazionali fra entity
bean differenti
- l'introduzione
di EJBQL un linguaggio standard per la definizione
delle procedure di ricerca tramite script da inserire
nel deployment file.
Concettualmente
le prime due modifiche sono molto importanti, mentre
il linguaggio di query, sebbene rappresenti un importante
passo in avanti nel processo di standardizzazione, non
costituisce una novità a tutti gli effetti dato
che i vari produttori già implementavano soluzioni
analoghe (ma essendo proprietarie limitavano fortemente
la portabilità delle applicazioni da un application
server all'altro).
Abstract Programming Schema
Così come avveniva con la versione 1.1 anche
in CMP 2.0 tutto il processo di sincronizzazione dello
stato di un entity viene gestito dal container. Adesso
però il programmatore possiede alcuni strumenti
in più per astrarre maggiormente l'applicazione
dal sistema sottostante di persistenza (container+database).
In CMP 2.0 nasce il concetto di campo virtuale (virtual
field), così chiamato perché, sebbene
faccia parte a tutti gli effetti del set di attributi
del bean, non è implementato direttamente dal
programmatore ma dal container al momento del deploy.
Con il termine di campo virtuale, ci si riferisce quindi
alla coppia di metodi set/get e non al campo in se che
non esiste prima del deploy. I campi virtuali si suddividono
poi in relazionali (vera innovazione di CMP 2.0) e di
persistenza.
L'insieme dei campi virtuali, ovvero dei corrispondenti
metodi astratti, danno luogo al concetto di Abstract
Programming Model. Considerando invece il codice XML
ad essi associati nel deployment descriptor si parla
di Abstract Programming Schema.
Adottare l' Abstract Programming Model significa trasformare
gli entity in classi astratte le cui implementazioni
verranno realizzate dal container al momento del deploy:
tali classi concrete sono dette classi di persistenza
ed includono in modo esplicito il codice relativo all'accesso
al database per realizzare il processo di persistenza
sui dati; tale codice è ottimizzando in funzione
del tipo e del prodotto specifico di database scelto.
Riconsiderando l'esempio della comunità virtuale
di MokaByte si potrebbe immaginare l'utilizzo di un
entity, CommunityUser che rappresenti un utente iscritto.
Tale bean conterrà una serie di campi virtuali
rappresentanti i dati dell'utente e come si avrà
modo di vedere in seguito potrebbe essere collegato
con altri entity con una legame di tipo relazionale.
La definizione della interfaccia locale potrebbe essere
public
interface CommunityUser extends javax.ejb.EJBLocalObject
{
public void setUserLastName(String userLastName);
public String getUserLastName();
public void setUserFirstName(String userFirstName);
public String getUserFirstName();
public void setUserEmailAddress(String userEmailAddress);
public String getUserEmailAddress();
public void setUserAddress(String userAddress);
public String getUserAddress();
public UserInfo getUSerInfo();
}
mentre
l'implementazione corrispondente, in ottica CMP 2.0,
abstract
public class CommunityUserBean implements EntityBean
{
EntityContext entityContext;
public String ejbCreate(String userLastName,
String
userFirstName,
String userEmailAddress,
String
userAddress)
throws
CreateException {
setUserLastName(userLastName);
setUserFirstName(userFirstName);
setUserEmailAddress(userEmailAddress);
setUserAddress(userAddress);
return null;
}
public java.lang.String ejbPostCreate(String
userLastName,
String
userFirstName,
String
userEmailAddress,
String
userAddress)
throws
CreateException {
}
public void ejbRemove() throws RemoveException
{
}
public abstract void setUserLastName(String
userLastName);
public abstract void setUserFirstName(String
userFirstName);
public abstract void setUserEmailAddress(String
usrEmailAddress);
public abstract void setUserAddress(String
userAddress);
public abstract String getUserLastName();
public abstract String getUserFirstName();
public abstract String getUserEmailAddress();
public abstract String getUserAddress();
public void ejbLoad() {
}
public void ejbStore() {
}
public void ejbActivate() {
}
public void ejbPassivate() {
}
public void unsetEntityContext() {
this.entityContext = null;
}
public void setEntityContext(EntityContext
entityContext) {
this.entityContext = entityContext;
}
public com.mokabyte.community.UserInfo getUSerInfo()
{
return null;
}
}
Si
noti come la classe sia contemporaneamente classe astratta
ed implementi la EntityBean. Definire l'entity ed i
suoi metodi astratti serve per rafforzare il fatto che
il bean non si possa considerare completo fino al momento
del deploy.
Il metodo getUserInfo() unitamente alla classe UserInfo
è particolarmente importante ai fini del pattern
progettuale della applicazione non della teoria EJB;
si parlerà più oltre di questi aspetti.
Come noto il metodo ejbPostCreate() entra in funzione
dopo la creazione del bean ma prima che questo sia disponibile
ai client: in questo caso, fra le varie cose questo
può essere utile per interagire con i campi relazionali,
dato che un istante prima essi non sono utilizzabili.
L'abstract
programming model e le interfacce remote
Tutti i metodi presenti nel bean che si desidera esporre
ai client remoti devono essere presenti nella interfaccia
remota: i metodi relativi all'Abstract Programming Model
però non è necessario siano inseriti nella
interfaccia remota, ed anzi in genere è preferibile
il contrario per limitare al minimo l'interfaccia remota
(e quindi il client-layer) dal meccanismo di persistenza
astratto.
Inoltre la specifica proibisce che siano esposti metodi
relativi ai campi relazionali: come si avrà modo
di vedere più avanti, questa regola alquanto
saggia, evita che si creino situazioni non coerenti
con lo schema relazionale del dominio dei dati.
Infine, come risulta ovvio, l'interfaccia remota non
può esporre metodi di modifica della chiave primaria
dell'entity.
Relativamente alla interfaccia home esistono importanti
differenze rispetto alla precedente specifica: il metodo
di ricerca per chiave primaria (findByPrimaryKey())
non è presente, dato che verrà implementato
al momento del deploy, mentre gli altri di ricerca verranno
definiti tramite la definizione di EJBQL inserite nel
file di deploy.
Deployment descriptor
Nel deployment descriptor sono state introdotte alcune
importanti novità, prima fra tutte il tag <cmp-version>
che serve per specificare la versione del CMP utilizzata.
Inserendo
<cmp-version>2.x<cmp-version>
si
specifica che si sta utilizzando il CMP 2.0, mentre
1.x è il valore da utilizzare per la versione
precedente. Il primo è il valore di default per
cui tale tag non è obbligatorio se si usa CMP
2.0.
Il resto del file XML è praticamente identico
a prima: ecco un esempio relativo al bean CommunityUser
<?xml
version="1.0" encoding="UTF-8"?>
<!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>
<display-name>CommunityUser</display-name>
<ejb-name>CommunityUser</ejb-name>
<local-home>com.mokabyte.community.CommunityUserHome</local-home>
<local>com.mokabyte.community.CommunityUser</local>
<ejb-class>com.mokabyte.community.CommunityUserBean</ejb-class>
<persistence-type>Container</persistence-type>
<prim-key-class>java.lang.String</prim-key-class>
<reentrant>False</reentrant>
<cmp-version>2.x</cmp-version>
<abstract-schema-name>Community</abstract-schema-name>
<cmp-field>
<field-name>userLastName</field-name>
</cmp-field>
<cmp-field>
<field-name>userFirstName</field-name>
</cmp-field>
<cmp-field>
<field-name>userEmailAddress</field-name>
</cmp-field>
<cmp-field>
<field-name>userAddress</field-name>
</cmp-field>
<primkey-field>userLastName</primkey-field>
</entity>
</enterprise-beans>
<assembly-descriptor>
<container-transaction>
<method>
<ejb-name>CommunityUser</ejb-name>
<method-name>*</method-name>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
</assembly-descriptor>
</ejb-jar>
Persistent fields e Dependent Classes
I campi di persistenza possono essere di tipo primitivo
(int, float, ..), tipi Java wrapper di primitivi (Byte,
Boolean, Short, Integer, Long, Double e Float) oltre
al tipo String e Date. Inoltre si possono utilizzare
anche tipi Java serializzabili definiti dall'utente:
in tal caso questi campi non vengono mappati con i tipi
del database ma sono salvati un qualche formato binario.
E' fortemente sconsigliato utilizzare questo genere
di oggetti, detti dependents classes, a meno che non
si debba memorizzare dati particolari come immagini
o dati multimediali; molto probabilmente questi campi
possono essere resi persistenti con i tipi standard
oppure con un legame relazionale con un altro entity
bean (di cui si parlerà nel prossimo articolo).
Per i tipi custom serializzabili vale la regola base
della serializzazione Java: gli oggetti non si spostano
ma verranno passati al client per copia (e non per reference
remota); per poter modificare tali attributi si dovrà
accedere ai metodi setXXX direttamente sull'entity bean.
Un buon utilizzo dei dependents object è quello
che li vede come oggetti di trasporto dati fra il client
ed il bean. Ad esempio invece di invocare i metodi getFirstName(),
getLastName(), getEmailAddress() etc. è preferibile
utilizzare un unico metodo getUserInfo() che ritorni
un bean serializzabile contenente tutte le informazioni
relative all'utente.
Ad esempio si potrebbe scrivere
public
com.mokabyte.community.UserInfo getUSerInfo() {
return new UserInfo(getUserFirstName(),
getUserLastName(),
getUserEmailAddress(),
getUserAddress());
}
Questo
metodo non è parte dell'Abstract Persistent Schema:
non è astratto ed il campo UserField non è
incluso nel deployment descriptor XML. Deve quindi considerarsi
come normale metodo remoto di business logic. Il tipo
ritornato da getUserInfo() in questo caso è UserInfo,
un oggetto serializzabile appositamente creato, ma si
potrebbe utilizzare anche un oggetto Properties (o collezioni
di vario tipo come Hashtable, List o Collection), oppure
un oggetto serializzabile di tipo custom.
Nel secondo caso si ha il vantaggio di non richiedere
al client la conoscenza della struttura del tipo ritornato
(l'oggetto UserInfo non deve essere presente sul client),
anche se il client deve conoscere esattamente il nome
degli attributi inseriti nella collection.
Utilizzare un oggetto custom offre invece una maggiore
potenza semantica: ad esempio si potrebbero eliminare
in UserInfo tutti i metodi di tipo setXXX, a sottolineare
che si tratta di un oggetto immutabile. Il client non
può effettuarvi modifiche, che peraltro non avrebbero
senso, visto che il tipo è ritornato per copia.
L'utilizzo
di metodi di questo genere deve essere pensata attentamente,
valutando l'ipotesi di altri pattern architetturali
(Session Façade, DAO, o altri descritti in [ejbdesign])
o di interfacce locali.
Dato che queste considerazioni esulano dagli scopi di
questo articolo, si rimanda alla lettura sui pattern
EJB (ottimo il libro [ejbdesign]) per maggiori approfondimenti.
Relational
fields
Infine un entity bean CMP 2.0 può contenere campi
virtuali che instaurano legami con altri entity. Tali
attributi sono detti relational fields e sono strettamente
legati ad una altra importante novità introdotta
in EJB 2.0, le interfacce locali (vedi [local]). Dato
che una completa trattazione di tali argomenti richiederebbe
più spazio di quello qui a disposizione, rimanderemo
la trattazione dei relational fields ad un prossimo
articolo.
Conclusione
In questo articolo si è iniziato a parlare del
nuovo modello di persistenza introdotto per i CMP nella
specifica 2.0 di EJB.
Da più parti gli entity beans ricevono molte
critiche per il loro negativo impatto sulle performance,
specialmente i CMP. Anche se non esiste una risposta
esaustiva ed univoca che possa dare conferma o smentita
di tali critiche, l'utilizzo dovrebbe essere subordinato
ad una attenta progettazione che faccia uso dei pattern
J2EE più importanti.
E' altresì vero che il nuovo modello offre una
serie di importanti strumenti tali da rendere il CMP
2.0 molto più potente flessibile ed astraendo
gli entity dalla particolare implementazione e dall'application
server utilizzato.
Bibliografia
[ejbspec] - " EJB 2.0 specification ", java.sun.com/products/ejb
[ejb] - " Enterprise Java Beans, sviluppare componenti
Enterprise Java ", di R. Monson-Hefel, Edizioni
Hops-O'Reilly.
[ejbdesgin] - "EJB Design Pattern" di Floy
Marinescu, ed. Wiley
[local] - "EJB 2.0: la Client Local API" di
Giovanni Puliti, MokaByte 70, Gennaio 2003, www.mokabyte.it/2003/03
|