[<<Retour] [Sommaire] [Suivant>>]
Cet exercice étend
l'exemple de l'exercice 1 pour utiliser un bean d'entité. BonusServlet appelle le bean d'entité pour enregistrer
et retrouver le numéro de sécurité sociale dans une table de base de
donnéé. Cette fonctionnalité d'accès à une base de donnée ajoute le
quatrième et dernier tiers à notre exemple d'architecture client léger
multi-tiers débuté dans l'exercice 1.
Le J2EE SDK est livré avec une base de données Cloudscape, et vous n'avez pas besoin de réglages supplémentaires dans votre environnement pour que le bean d'entité puisse accéder à la base. En fait dans cet exemple, vous n'aurez pas à écrire de code SQL ou JDBC pour créer la base de donnée ou effectuer les opérations d'accès à la base de donnée. La table est crée et le code SQL est généré par la Deployment tool pendant l'assemblage et le déploiement. L'exercice 7 Technologie JDBC et persistance géréé par le bean montre comment écrire le code SQL pour un bean d'entité.
Un bean d'entité représente des données persistantes stockées dans une ligne de table de base de données. Quand un bean d'entité est crée, les données sont écrites dans la table de base de données appropriée, et si les données d'un bean d'entité sont modifiées, les données dans la ligne de la table de base de donnée sont aussi modifiées. La création de la table dans la base de donnée et les mises à jour de ligne interviennent toutes sans que vous ayez à écrire la moindre ligne de code SQL ou JDBC.
Les données d'un bean d'entité sont persistantes car elles surivivent à un plantage.
La principale différence
entre le bean de session CalcHome de
l'exercice 1 et le bean d'entité BonusHome
de cette exercice (voir plus bas) est la méthode
findByPrimaryKey
. Cette méthode de recherche prend une clé primaire comme paramètre. Dans cet exemple, la
clé primaire est un numéro de sécurité sociale, qui est utilisé pour
retrouver une ligne de table de base de donnée correspondant au numéro
de sécurité sociale passé à la méthode.
La méthode create
prend une valeur de bonus et une clé primaire comme paramètre. Quand
la BonusServlet instantie l'interface home
et appelle sa méthode create, le conteneur
crée un instance de BonusBean et appelle sa
méthode ejbCreate . Les méthodes
BonusHome.create et
BonusBean.ejbCreate doivent avoir les mêmes signatures, pour que les valeurs du bonus et de la
clé primaire puissent être passées de l'interface home au bean
d'entité par le conteneur du bean d'entité. Si la ligne pour un numéro
donné de clé primaire
(sécurité sociale) existe déjà, une
java.rmi.RemoteException est déclenchée qui est captée par le
code client de la BonusServlet.
package Beans;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.FinderException;
import javax.ejb.EJBHome;
public interface BonusHome extends EJBHome {
public Bonus create(double bonus, String socsec)
throws CreateException, RemoteException;
public Bonus findByPrimaryKey(String socsec)
throws FinderException, RemoteException;
}
Après que l'interface
home soit crée, le container crée l'interface remote et le bean
d'entité. L'interface Bonus déclare les
méthodes getBonus et getSocSec pour que la servlet
puisse accéder aux données du bean d'entité.
package Beans;
import javax.ejb.EJBObject;
import java.rmi.RemoteException;
public interface Bonus extends EJBObject {
public double getBonus() throws RemoteException;
public String getSocSec() throws RemoteException;
}
BonusBean est un bean d'entié géré par le conteneur. Cela veut dire
que le conteneur s'occupe de la persistance des données et de la
gestion des transactions sans que vous ayez à écrire le code pour le
transfert des données entre le bean d'entité et la base de données ou
pour définir les limites des transactions.
Si pour une raison ou une
autre vous souhaitez que votre bean d'entité gére lui-même sa
persistance ou ses transactions, vous pouvez fournir des
implémentations pour quelques unes des méthodes vides de BonusBean ci après. Les liens suivants vous
emménent sur des documents qui présentent la persistance et les
transactions gérées par le bean.
developer.java.sun.com/developer/onlineTraining/Programming/JDCBook
java.sun.com/j2ee/j2sdkee/techdocs/guides/ejb/html/DevGuideTOC.html
Quand
BonusServlet appelle BonusHome.create
, le container appelle la méthode
BonusBean.setEntityContext. L'instance d'EntityContext passée à la méthode
setEntityContext a des méthodes qui permettent à votre bean de
renvoyer une référence de lui-même ou d'obtenir sa clé primaire.
Après, le conteneur
appelle la méthode ejbCreate . La méthode
ejbCreate assigne des valeurs aux variables
de l'instance du bean, puis enregistre ces données dans la base de
données. La méthode ejbPostCreate est
appelée après la méthode ejbCreate et
exécute toutes les traitements requis après la création du bean. Cet
exemple simple ne nécessite pas de traitement de postcréation.
Les autres méthodes vides sont des appels de fonctions utilisés par le conteneur pour notifier au bean qu'un événement est arrivé. Vous pouvez fournir un comportement pour certaines de ces méthodes si vous utilisez la persistance gérée par le bean, et d'autres si vous avez besoin de fournir des spécifications précises pour la maintenance ou l'initialisation du bean. Ces opération de maintenance - nettoyage et initialisations prennent place à des moments précis du cycle de vie du bean, et le conteneur prévient et déclenche les méthodes nécessaires au moment opportun. Voici une brève explication de ces méthodes:
ejbPassivate et
ejbActivate sont appelées par le conteneur avant que le
conteneur charge et décharge le bean sur le support de stockage. Ce
procédé est similaire au concept de mémoire virtuelle avec le
chargement/déchargement de données entre la mémoire vive et le
disque dur.
ejbRemove si l'interface home
a une méthode remove correspondante qui
peut être appelée par le client.
ejbLoad et ejbStore
sont appelées par le conteneur avant qu'il synchronise l'état du bean dans la base de données sous-jacente.
Les méthodes getBonus et getSocSec sont
appelées par les clients pour accéder aux données stockées dans les
variables de l'instance. Cet exemple n'a pas de méthode set< type
> , mais si c'était le cas, les clients pourraient les
appeler pour modifier les données dans les propriétés (variables) de
l'instance. Tout changement dans les propriétés de l'instance résulte
en une mise à jour de la ligne de table de base de données
correspondante.
package Beans;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.EntityBean;
import javax.ejb.EntityContext;
public class BonusBean implements EntityBean {
public double bonus;
public String socsec;
private EntityContext ctx;
public double getBonus() {
return this.bonus;
}
public String getSocSec() {
return this.socsec;
}
public String ejbCreate(double bonus,
String socsec)
throws CreateException{
//Appelé par le conteneur après setEntityContext
this.socsec=socsec;
this.bonus=bonus;
return null;
}
public void ejbPostCreate(double bonus,
String socsec) {
//Appelé par le conteneur après ejbCreate
}
//Les méthodes suivantes sont des appels de fonctions
//appelés par le conteneur pour avertir
//le bean qu'un événement à eu lieu
public void ejbActivate() {
//Appelé par le conteneur avant que le Bean
//rentre en mémoire
}
public void ejbPassivate() {
//Appelé par le conteneur avant que le bean
//soit stocké
}
public void ejbRemove() throws RemoteException {
//Appelé par le conteneur avant que
//les données soit supprimées de la base de données
}
public void ejbLoad() {
//Appelé par le conteneur
//pour rafraîchir l'éatat du bean d'entité
}
public void ejbStore() {
//Appelé par le conteneur
//pour sauver l'état du bean dans la base de données
}
public void setEntityContext(EntityContext ctx){
//Appelé par le conteneur pour définir le contexte du Bean
}
public void unsetEntityContext(){
//Appelé par le conteneur pour enlever le contexte du Bean
}
}
Le code de la
BonusServlet pour cette exercice est très similaire à celui de
l'exercice 1 avec des modifications dans les méthodesinit et doGet . La méthode
init pour cet exercice utilise à la fois le
bean de session CalcBean, et le bean
d'entité BonusBean .
public class BonusServlet extends HttpServlet {
CalcHome homecalc;
BonusHome homebonus;
Bonus theBonus, record;
public void init(ServletConfig config)
throws ServletException{
try {
InitialContext ctx = new InitialContext();
Object objref = ctx.lookup("bonus");
Object objref2 = ctx.lookup("calcs");
homebonus=(
BonusHome)PortableRemoteObject.narrow(
objref, BonusHome.class);
homecalc=(CalcHome)
PortableRemoteObject.narrow(
objref2, CalcHome.class);
} catch (Exception NamingException) {
NamingException.printStackTrace();
}
}
La clause try dans la méthode doGet crée
des interfaces home pour des CalcBean et
BonusBean . Après avoir appelé
calcBonus pour calculer le bonus, la méthode
BonusHome.create est appelée pour créer une
instance de bean d'entité et la ligne correspondante dans la table de
la base de données sous-jacente. Après la création de la table, la
méthode BonusHome.findByPrimaryKey est
appelée pour obtenir le même enregistrement par sa clé primaire
(numéro de sécurité sociale). Après, une page HTML est renvoyée au
navigateur web avec les données transmises initialement, le bonus
calculé, et les données reçues de la base de données.
La clause catch intercepte et traite les clés primaires (numéro de
sécurité sociale) multiples. La base de donnée ne peut avoir deux
lignes avec la même clé primaire, c'est pourquoi si vous passez deux
fois le même numéro de sécurité sociale à la servlet, la servlet
intercepte et traite l'erreur avant de créer le bean d'entité. Dans le
cas d'une clé multiple (duplicate key), la servlet renvoit une page
HTML avec les données passées à l'origine, le bonus calculé et un
message d'erreur.
try {
Calc theCalculation;
//Récupérer les informations de bonus et numéro de sécurité
String strMult = request.getParameter(
"MULTIPLIER");//Calculer le bonus
Integer integerMult = new Integer(strMult);
multiplier = integerMult.intValue();
socsec = request.getParameter("SOCSEC");
//Calculer le bonus
double bonus = 100.00;
theCalculation = homecalc.create();
calc = theCalculation.calcBonus(
multiplier, bonus);
//Créer une ligne dans la table
theBonus = homebonus.create(calc, socsec);
record = homebonus.findByPrimaryKey(socsec);
//Afficher les données
out.println("<H1>Bonus Calculation</H1>");
out.println("<P>Soc Sec passed in: " +
theBonus.getSocSec() + "<P>");
out.println("<P>Multiplier passed in: " +
multiplier + "<P>");
out.println("<P>Bonus Amount calculated: " +
theBonus.getBonus() + "<P>");
out.println("<P>Soc Sec retrieved: " +
record.getSocSec() + "<P>");
out.println("<P>Bonus Amount retrieved: " +
record.getBonus() + "<P>");
out.println("</BODY></HTML>");
//Intercepter le message d'erreur sur les clés dupliquées
} catch (javax.ejb.DuplicateKeyException e) {
String message = e.getMessage();
//Afficher les données
out.println("<H1>Bonus Calculation</H1>");
out.println("<P>Soc Sec passed in: " +
socsec + "<P>");
out.println("<P>Multiplier passed in: " +
multiplier + "<P>");
out.println("<P>Bonus Amount calculated: " +
calc + "<P>");
out.println("<P>" + message + "<P>");
out.println("</BODY></HTML>");
} catch (Exception CreateException) {
CreateException.printStackTrace();
}
}
Tout d'abord, compiler le bean d'entité et la servlet. Se reporter à l'exercice 1 pour le paramétrage du PATH et du CLASSPATH et les informations sur l'endroit où placer les fichiers sources.
#!/bin/sh
cd /net/home/lmaitre/J2EE
J2EE_HOME=/net/home/lmaitre/J2EE/j2eesdk1.3
CPATH=.:$J2EE_HOME/lib/j2ee.jar
javac -d . -classpath "$CPATH" Beans/BonusBean.java
Beans/BonusHome.java Beans/Bonus.java
cd \home\monicap\J2EE
set J2EE_HOME=\home\monicap\J2EE\j2eesdk1.3
set CPATH=.;%J2EE_HOME%\lib\j2ee.jar
javac -d . -classpath %CPATH% Beans/BonusBean.java
Bean s/BonusHome.java Beans/Bonus.java
cd /net/home/lmaitre/J2EE/ClientCode J2EE_HOME=/net/home/lmaitre/J2EE/j2eesdk1.3 CPATH=.:$J2EE_HOME/lib/j2ee.jar:/net/home/lmaitre/J2EE javac -d . -classpath "$CPATH" BonusServlet.java
cd \home\monicap\J2EE\ClientCode
set J2EE_HOME=\home\monicap\J2EE\j2eesdk1.3
set CPATH=.;%J2EE_HOME%\lib\j2ee.jar;
\home\monicap\J2EE
javac -d . -classpath %CPATH% BonusServlet.java
Pour exécuter cet exemple vous avez besoin de démarrer le serveur J2EE, l'outil de déploiement et la base de donnée Cloudscape. Dans des fenêtres différentes, tapez les commandes suivantes :
j2ee -verbose deploytool cloudscape -start
Si cela ne marche pas,
tapez ceci à partir du répertoire J2EE :
j2eesdk1.3/bin/j2ee -verbose j2eesdk1.3/bin/deploytool j2eesdk1.3/bin/cloudscape -start
j2eesdk1.3\bin\j2ee -verbose j2eesdk1.3\bin\deploytool j2eesdk1.3\bin\cloudscape -start
Les étapes de cette sections sont :
Le fichier web archive
(WAR) contient BonusServlet et bonus.html . Comme vous avez changé
BonusServlet , vous devez mettre à jour l'application J2EE avec
le nouveau code de la servlet.
Note:
L'application BonusApp de la leçon
précédente est automatiquement désinstallée
Les étapes pour créer le fichier JAR de l'EJB sont très ressemblantes aux étapes pour le bean de session vues dans l'exercice 1. Il y a quelques différences cependant et ces différences sont expliquées ici.
Note: Dans cet exercice le bean d'entité va dans un fichier JAR différent de celui du bean de session de l'exercice 1 pour continuer l'exemple de l'exercice 1 avec le minimum de changements. Comme ces beans ont des fonctionnalités liées on devrait pourtant les livrer et les déployer dans le même fichier JAR. Vous verrez comment livrer des beans liès dans le même fichier JAR dans l'exercice 3.
BonusApp apparaisse dans le champ
Create New JAR File in Application .
BonusJar comme nom (JAR Display name).
Edit... (situé à droite de la fentêtre Contents).
Bonus.class
Add .
BonusBean.class
Add .
BonusHome.class
Add .
OK .
Beans.BonusBean est le nom de la classe (classname)
Beans.BonusHome est l'interface home
Beans.Bonus est la remote interface.
BonusBean comme nom (display name).
Next .
Container-Managed persistence .
bonus et
socsec .
java.lang.String pour la classe de la clé
primaire. Notez que la clé primaire doit être d'un type classe. Les
types primitifs ne sont pas valides pour les clés primaires.
socsec comme nom pour la clé primaire.
Next .
Next . Ce bean d'entité simple ne dépend pas
d'une base de donnée ou d'un objet session JavaMail.
Container-managed transactions (si ce n'est pas
déjà sélectionné).
create , findByPrimaryKey ,
getBonus et getSocSec . Cela veut
dire que le conteneur débutera une nouvelle transaction avant
d'exécuter ces méthodes. La transactions est effective juste avant
que la méthode s'achéve. Plus d'infomration sur le paramétrage des
transactions sont disponibles dans le chapitre 6 du Enterprise
JavaBeans Developer's Guide (java.sun.com/j2ee/j2sdkee/techdocs/guides/ejb/html/DevGuideTOC.html
).
BonusApp .
JNDI names
bonus comme nom JNDI pour
BonusBean
Avant que l'application J2EE puissent être déployée, vous avez besoin de préciser les paramètres de déploiement pour le bean d'entité et générer le SQL. Voici comment faire :
jdbc/Cloudscape (avec un C majuscule
à Cloudscape) pour le nom JNDI de la base de donnée (Database JNDI
name)
Create table on deploy et Delete table on Deploy sont cochées.
Generate SQL .
Note: Si vous obtenez une erreur indiquant que la connexion a été refusée, démarrer la base de donnée comme indiqué dans Démarrer les serveurs et les outils.
findByPrimaryKey dans la boîte EJB method. A droite une
expression SQL apparaît. Il doit indiquer SELECT
"socsec" FROM "BonusBeanTable" WHERE "socsec"=? . Le point
d'interrogation (?) représente le paramètre passé à la méthode
findByPrimaryKey .
OK .
Verifier dans le
menu Tools .
OK .
La fenêtre doit vous indiquer qu'aucun test n'a échoué.
Note:
Daans la version 1.2 du J2EE SDK vous pouvez avoir une erreur tests app.WebURI . L'application J2EE se
déploiera malgré cela.
Note: Ne pas cocher la case Return Client Jar. La seule raison de cocher cette case est quand vous voulez utiliser la persistance gérée par le bean ou déploer une application autonome comme programme client. Cet exemple utilise une servlet et une âge HTMl donc ces cases ne doivent pas être cochées. Cocher ces cases crée un fichier JAR avec les informations de déploiement nécessaires pour une application autonome.
Next . S'assurer que le nom JNDI affiche calcs pour CalcBean
et bonus pour
BonusBean . Saisir tout nom manquant par vous-même, et
appuyer sur la touche Entrée .
Next . S'assurer que le nom du contexte racine
(Context Root) affiche BonusRoot . Si ce
n'est pas le cas, le saisir vous-même et appuyer sur la touche
Entrée .
Next .
Finish pour démarrer le déploiement.
OK .
Le serveur web s'exécute sur
le port 8000 par défaut. Pour ouvrir la page bonus.html
aller avec votre navigateur à l'adresse http://localhost:8000/BonusRoot/bonus.html , qui est l'endroit
où la Deployment tool à mis le fichier HTML.
Saisir un numéro de
sécurité sociale et un multiplicateur et cliquer sur le bouton Submit . BonusServlet
traite vos données et renvoie une page HTML avec le résultat du calcul
du bonnus.
Bonus Calculation Soc Sec passed in: 777777777 Multiplier passed in: 25 Bonus Amount calculated: 2500.0 Soc Sec retrieved: 7777777777 Bonus Amount retrieved: 2500.0
Si vous retournez sur
bonus.html et changez le multiplicateur en 2
mais utilisez le même numéro de sécurité sociale, vous obtenez ceci :
Bonus Calculation Soc Sec passed in: 777777777 Multiplier passed in: 2 Bonus Amount calculated: 200.0 Duplicate primary key.
[TOP]