Transparents

publicité
15 Développement d'application
de base de données en Java
21/04/2017
© Robert Godin. Tous droits réservés.
1
Patron d'architecture en
couche (layer)




Couche présentation
Couche contrôle (coordonnateur d'application)
Couche domaine d'application (ou métier)
Couche de services.
–
–
–
–
–
21/04/2017
persistance
transaction
communication
sécurité
etc.
© Robert Godin. Tous droits réservés.
2
15.1 Application
Java/JDBC client-serveur
BD
Réseau
Programme
d'application
Logiciel
intermédiaire
Logiciel
intermédiaire
Pilote de
télécommunication
Pilote de
télécommunication
Client
Programme d'application :
- présentation
-contrôle
-domaine d'application
SGBD
Serveur
SGBD
- persistence
- partie du domaine d'application ?
Client
21/04/2017
© Robert Godin. Tous droits réservés.
Serveur
3
Cas d ’utilisation
EnregistrerPrêts
Commis au prêt
Système
1. Le commis invoque l'écran d'enregistrement du
prêt
2. Le système demande la saisie de l'identificateur de
l'utilisateur
3. Le commis saisit l'identificateur de l'utilisateur à
l'aide de la carte de membre ou manuellement
4. Le système affiche le nombre de prêts en cours et
demande de saisir l'identificateur de l'exemplaire.
5. Le commis entre l'identificateur de l'exemplaire à
l'aide du lecteur optique ou manuellement
6. Le système enregistre le prêt, affiche un message
d'acquiescement.
21/04/2017
© Robert Godin. Tous droits réservés.
4
Cas de test pour scénario normal
21/04/2017
© Robert Godin. Tous droits réservés.
5
Cas d ’exception : nombre
maximal d'emprunts (ici 2) atteint
21/04/2017
© Robert Godin. Tous droits réservés.
6
Cas d ’exception :
exemplaire non disponible
21/04/2017
© Robert Godin. Tous droits réservés.
7
Cas d'utilisation et classes
métiers concernées
Exemplaire
Personne
nom : String
pré nom : String
{UNIQUE: idExemplaire}
idExemplaire : String
dateAchat : Date
statut : enum(prêté, disponible, retiré)
1
Utilisateur
0..*
Prêt
{UNIQUE :idUtilisateur}
idUtilisateur : String
1
motPasse : String
EnregistrerPrêts
(from PackageCasServicePrêt)
catégorieUtilisateur
{disjointe, complète}
0..*
datePrêt : Date
{disjointe, complète}
PrêtEnCours
21/04/2017
Empl oyé
Membre
{UNIQUE : cod eMatricule}
codeMatri cul e : String
catégori eEm ployé : enum (b ibl io thécaire, co mm is )
téléphoneRésidence : String
nbMaxPrêts : Integer = 5
duréeMaxPrêts : Integer = 7
© Robert Godin. Tous droits réservés.
8
Patron courtier BD : classes du
programme Java et tables concernées
~ Data Access Object
(DAO) + Domain Data
Transfer Object
Person ne
nom : Stri ng
préno m : Stri ng
Utilisateur
idUtilisateur : String
motPasse : String
catégorie : String
lesPrêts : Vector
Employé
Membre
{UNIQUE : codeMatricule}
codeMatricule : String
catégorieEmployé : enum(bibliothécaire, commis)
té léph oneRésidence : String
nbMaxPrêts : Integer = 5
duréeMaxPrêts : Integer = 7
Exemplaire
idExemplaire : String
statut : String
PrêtEnCours
CourtierBDUtilisateur
CourtierBDPrêtsEnCou rs
Courtie rBDExem plaire
ch erch erVari ables Stati quesDeMem bre()
ch erch erUtili sateu rParIdUti lisateur()
chercherLesPrêtsEnCours()
insérerPrêtEnCours ()
che rche rExemplai reParId Exemplaire()
Table MembreGénéral
Table Utilisateur
Table Emp loyé
21/04/2017
Prêt
datePrêt : Date
lExemplaire : Exemplaire
lUtilisateur : Utilisateur
Table PrêtsEnCours
Table Exemplaire
Table Membre
© Robert Godin. Tous droits réservés.
9
Classe Java pour
Personne
package ExemplesJDBC.SyLeRat;
public class Personne {
protected String nom;
protected String prénom;
public Personne(String nom,String prénom) {
this.nom = nom;
this.prénom = prénom;
}
public String getNom(){return nom;}
public String getPrénom(){return prénom;}
public void setNom(String nom){this.nom = nom;}
public void setPrénom(String prénom){this.prénom = prénom;}
}
Lecteurs/modifieurs pour attributs
(peut en faire un Java Bean)
21/04/2017
© Robert Godin. Tous droits réservés.
10
Classe Java pour
Utilisateur
package ExemplesJDBC.SyLeRat;
import java.util.*;
public class Utilisateur extends Personne {
protected String idUtilisateur;
protected String motPasse;
protected String catégorie;
protected Vector lesPrêts; // Association avec Prêt
// NB Ce constructeur de crée pas lesPrêts
public Utilisateur(String idUtilisateur, String motPasse, String nom,
String prénom, String catégorie){
super(nom,prénom);
this.idUtilisateur = idUtilisateur;
this.motPasse = motPasse;
this.catégorie = catégorie;
}
public String getIdUtilisateur(){return idUtilisateur;}
public String getMotPasse(){return motPasse;}
public String getCatégorie(){return catégorie;}
public Vector getLesPrêts(){return lesPrêts;}
public int getNbPrêtsEnCours(){
Enumeration enumerationPrêts = lesPrêts.elements();
int nbPrêtsEnCours = 0;
while (enumerationPrêts.hasMoreElements()){
Prêt unPrêt = (Prêt)enumerationPrêts.nextElement();
if (unPrêt instanceof PrêtEnCours){
nbPrêtsEnCours++;
}
}
return nbPrêtsEnCours;
}
public
public
public
public
}
21/04/2017
void
void
void
void
Lecteurs/modifieurs
pour naviguer les
associations
Lecteur pour donnée
dérivée
setIdUtilisateur(String idUtilisateur){this.idUtilisateur = idUtilisateur;}
setMotPasse(String motPasse){this.motPasse = motPasse;}
setCatégorie(String catégorie){this.catégorie = catégorie;}
setLesPrêts(Vector lesPrêts){this.lesPrêts = lesPrêts;}
© Robert Godin. Tous droits réservés.
11
Classe Java pour
Membre
package ExemplesJDBC.SyLeRat;
import java.util.*;
public class Membre extends Utilisateur {
private static int nbMaxPrêts;
private static int duréeMaxPrêt;
private String téléphoneRésidence;
public Membre(String idUtilisateur, String motPasse, String nom,
String prénom, String catégorie, String téléphoneRésidence){
super(idUtilisateur, motPasse, nom, prénom, catégorie);
this.téléphoneRésidence = téléphoneRésidence;
}
public
public
public
public
21/04/2017}
static
static
static
static
Attributs et méthodes
static pour attributs de
classe (ou singleton)
int getNbMaxPrêts(){return nbMaxPrêts;}
int getDuréeMaxPrêt(){return duréeMaxPrêt;}
void setNbMaxPrêts(int leNbMaxPrêts){nbMaxPrêts = leNbMaxPrêts;}
void setDuréeMaxPrêts(int laDuréeMaxPrêt){duréeMaxPrêt = laDuréeMaxPrêt;}
public void setTéléphoneRésidence(String téléphoneRésidence)
{this.téléphoneRésidence = téléphoneRésidence;}
public String getTéléphoneRésidence(){return téléphoneRésidence;}
public int getNbRetards(){
Enumeration enumerationPrêts = lesPrêts.elements();
int nbRetards = 0;
Calendar maintenant = Calendar.getInstance(); // Calendrier avec date actuelle
// Soustrait ensuite la durée maximale en jours pour obtenir la date minimale
// avant laquelle il y a un retard
maintenant.add(Calendar.DATE,-duréeMaxPrêt);
java.sql.Date dateMinSansRetard = new java.sql.Date(maintenant.getTime().getTime());
while (enumerationPrêts.hasMoreElements()){
Prêt unPrêt = (Prêt)enumerationPrêts.nextElement();
if (unPrêt instanceof PrêtEnCours){
if (dateMinSansRetard.after(unPrêt.getDatePrêt())){
nbRetards++;
}
}
}
return nbRetards;
}
public boolean conditionsPrêtAcceptées(){
return (this.getNbRetards() == 0 && this.getNbPrêtsEnCours() < nbMaxPrêts);
}
© Robert Godin. Tous droits réservés.
12
Classe Java pour
Employé
package ExemplesJDBC.GererPrets;
import java.util.*;
public class Employé extends Utilisateur {
private String codeMatricule;
private String catégorieEmployé;
public Employé(String idUtilisateur, String motPasse, String nom,
String prénom, String catégorie, String codeMatricule, String catégorieEmployé){
super(idUtilisateur, motPasse, nom, prénom, catégorie);
this.codeMatricule = codeMatricule;
this.catégorieEmployé = catégorieEmployé;
}
public String getCodeMatricule(){return codeMatricule;}
public String getCatégorieEmployé(){return catégorieEmployé;}
public void setCodeMatricule(String codeMatricule)
{this.codeMatricule = codeMatricule;}
public void setCatégorieEmployé(String catégorieEmployé)
{this.catégorieEmployé = catégorieEmployé;}
}
21/04/2017
© Robert Godin. Tous droits réservés.
13
Classe Java pour Prêt
package ExemplesJDBC.SyLeRat;
import java.sql.Date;
public class Prêt {
protected Utilisateur lUtilisateur;
protected java.sql.Date datePrêt;
protected Exemplaire lExemplaire;
public Prêt(Utilisateur lUtilisateur,java.sql.Date datePrêt, Exemplaire lExemplaire) {
this.lUtilisateur = lUtilisateur;
this.datePrêt = datePrêt;
this.lExemplaire = lExemplaire;
}
public java.sql.Date getDatePrêt(){return datePrêt;}
public String getIdExemplaire(){return lExemplaire.getIdExemplaire();}
public String getIdUtilisateur(){return lUtilisateur.getIdUtilisateur();}
}
21/04/2017
© Robert Godin. Tous droits réservés.
14
Classe Java pour
PrêtEnCours
package ExemplesJDBC.GererPrets;
public class PrêtEnCours extends Prêt {
public
PrêtEnCours(Utilisateur
lUtilisateur,java.sql.Date
datePrêt,
lExemplaire) {
super(lUtilisateur,datePrêt,lExemplaire);
}
// Le constructeur suivant ne construit pas le lien vers Exemplaire
public PrêtEnCours(Utilisateur lUtilisateur,java.sql.Date datePrêt) {
super(lUtilisateur,datePrêt,null);
}
}
21/04/2017
© Robert Godin. Tous droits réservés.
Exemplaire
15
Classe Java pour
Exemplaire
package ExemplesJDBC.SyLeRat;
public class Exemplaire {
private String idExemplaire;
private String statut;
public Exemplaire(String idExemplaire,String statut) {
this.idExemplaire = idExemplaire;
this.statut = statut;
}
public String getStatut(){return statut;}
public String getIdExemplaire(){return idExemplaire;}
}
21/04/2017
© Robert Godin. Tous droits réservés.
16
CourtierBDUtilisateur
package ExemplesJDBC.GererPrets;
import java.sql.*;
import java.util.*;
public class CourtierBDUtilisateur{
private Connection uneConnection;
// Constructeur pour connexion passée par le créateur
public CourtierBDUtilisateur(Connection laConnection){
this.uneConnection = laConnection;
}
…
public Utilisateur chercherUtilisateurParIdUtilisateur(String idUtilisateur)
// Retourne un objet Membre ou un Employé selon le cas
throws Exception {
// Verrouillage de l'utilisateur pour sérialisabilité (Oracle mode READ COMMITTED)
PreparedStatement unEnoncéSQL = uneConnection.prepareStatement
("SELECT motPasse,nom,prénom,catégorieUtilisateur,téléphoneRésidence,codeMatricule,catégorieEmployé "+
"FROM Membre m, Utilisateur u, Employé e WHERE u.idUtilisateur = ? AND "+
"u.idUtilisateur = m.idUtilisateur (+) AND "+
"u.idUtilisateur = e.idUtilisateur (+) FOR UPDATE");
unEnoncéSQL.setString(1,idUtilisateur);
ResultSet résultatSelect = unEnoncéSQL.executeQuery();
if (résultatSelect.next ()){
if (résultatSelect.getString ("catégorieUtilisateur").equals("membre")){
Membre leMembre = new Membre(
idUtilisateur,résultatSelect.getString ("motPasse"),
résultatSelect.getString ("nom"),résultatSelect.getString ("prénom"),
résultatSelect.getString ("catégorieUtilisateur"),
résultatSelect.getString ("téléphoneRésidence"));
unEnoncéSQL.close();
return leMembre;
}
else{// Pas un Membre, doit être un Employé
Employé lEmployé = new Employé(idUtilisateur,résultatSelect.getString ("motPasse");
…
21/04/2017
© Robert Godin. Tous droits réservés.
17
Diagramme de séquence
pour CourtierBDUtilisateur
: ContrôleEnregistrerPrêts
: CourtierBDUtilisateur
:j ava.sql .Connection
1. «create»(uneConnection)
:java.sql.Statement
2. chercherUtilisateur(idUtilisateur)
3. prepareState ment()
4. executeQuery()
: Membre
: Em pl oyé
5. [catégorie = 'membre']«create»
6. [catégorie='employé']«create»
7. unU til isateur
21/04/2017
© Robert Godin. Tous droits réservés.
18
Classe UsineConnection
package ExemplesJDBC.GererPrets;
import java.sql.*;
public class UsineConnection {
public UsineConnection() {}
public Connection getConnectionSansAutoCommit(
String nomPilote,
// "oracle.jdbc.driver.OracleDriver" pour Oracle
String URLBD,
// exemple : "jdbc:oracle:thin:@localhost:1521:ora817i"
String authorizationID,
String password)
throws Exception {
Class.forName (nomPilote);
Connection uneConnection = DriverManager.getConnection (URLBD, authorizationID,password);
uneConnection.setAutoCommit(false);
return uneConnection;
}
public Connection getConnection(
String nomPilote,
// "oracle.jdbc.driver.OracleDriver" pour Oracle
String URLBD,
// exemple : "jdbc:oracle:thin:@localhost:1521:ora817i"
String authorizationID,
String password)
throws Exception {
Class.forName (nomPilote);
Connection uneConnection = DriverManager.getConnection (URLBD, authorizationID,password);
return uneConnection;
}
}
21/04/2017
© Robert Godin. Tous droits réservés.
19
Courtier CourtierBDPrêtEnCours :
chercherLesPrêtsEnCours
package ExemplesJDBC.GererPrets;
import java.sql.*;
import java.util.*;
public class CourtierBDPrêtEnCours {
private Connection uneConnection;
…
public void chercherLesPrêtsEnCours(Utilisateur unUtilisateur)
// Dématérialiser les PrêtEnCours d'un Utilisateur
throws Exception{
// Verrouiller la table des prêts en cours pour sérialisabilité
// (Oracle mode READ COMMITTED)
PreparedStatement unEnoncéLock = uneConnection.prepareStatement(
"LOCK TABLE PrêtEnCours IN SHARE ROW EXCLUSIVE MODE");
unEnoncéLock.execute();
unEnoncéLock.close();
PreparedStatement unEnoncéSQL = uneConnection.prepareStatement
("SELECT datePrêt,idExemplaire "+
"FROM PrêtEnCours WHERE idUtilisateur = ? " );
String idUtilisateur = unUtilisateur.getIdUtilisateur();
unEnoncéSQL.setString(1,idUtilisateur);
ResultSet résultatSelect = unEnoncéSQL.executeQuery();
// lesPrêts contiendra la collection des objets PrêtEnCours
Vector lesPrêts = new Vector();
while (résultatSelect.next ()){
PrêtEnCours unPrêt = new PrêtEnCours(unUtilisateur,résultatSelect.getDate(1));
lesPrêts.addElement(unPrêt);
}
// Faire les références inverses !
unUtilisateur.setLesPrêts(lesPrêts);
unEnoncéSQL.close();
}
…
}
21/04/2017
© Robert Godin. Tous droits réservés.
20
Matérialisation d ’un PrêtEnCours
: insérerPrêtEnCours
package ExemplesJDBC.GererPrets;
import java.sql.*;
import java.util.*;
public class CourtierBDPrêtEnCours {
private Connection uneConnection;
…
public void insérerPrêtEnCours (PrêtEnCours unPrêtEnCours)
// Matérialise un nouveau PrêtEnCours dans la BD
throws Exception, SQLException {
PreparedStatement unEnoncéSQL = uneConnection.prepareStatement
("INSERT INTO PrêtEnCours(idExemplaire,idUtilisateur)"+
"VALUES(?,?)");
unEnoncéSQL.setString(1,unPrêtEnCours.getIdExemplaire());
unEnoncéSQL.setString(2,unPrêtEnCours.getIdUtilisateur());
//NB la date est générée automatiquement par le serveur de BD (default sysdate)
// et le statut est modifié par un TRIGGER
int n = unEnoncéSQL.executeUpdate();
unEnoncéSQL.close();
if (n != 1){throw new Exception("Insertion dans PrêtEnCours a échoué");}
}
}
21/04/2017
© Robert Godin. Tous droits réservés.
21
Enjeux de la conception
des courtiers
Granularité des courtiers
 Granularité de
matérialisation/dématérialisation

–
–
21/04/2017
minimiser les appels au serveur de BD
limiter le volume de données à transférer
© Robert Godin. Tous droits réservés.
22
Classe de contrôle
ControleEnregistrerPrets
package ExemplesJDBC.GererPrets;
/* Classe de contrôle simple pour EnregistrerPrêts
* Interface à l'utilisateur minimaliste
* NB Vérifie les conditions de prêt et le statut de l'exemplaire au niveau du programme client
*/
import java.sql.*;
import javax.swing.JOptionPane;
import java.util.*;
class ControleEnregistrerPrets {
public static void main (String args []) throws Exception {
// Création d'une Connection globale pour l'application
UsineConnection uneUsineConnection = new UsineConnection();
Connection uneConnection = uneUsineConnection.getConnectionSansAutoCommit(
"oracle.jdbc.driver.OracleDriver",
"jdbc:oracle:thin:@localhost:1521:ora817i",
"clerat","oracle");
try{
// Dématérialiser l'Utilisateur et ses PrêtsEnCours
Membre unMembre = null;
String idUtilisateur =
JOptionPane.showInputDialog("Entrez l'identificateur de l'utilisateur: ");
// Création du courtier et dématérialisation de l'Utilisateur
CourtierBDUtilisateur unCourtierBDUtilisateur = new CourtierBDUtilisateur(uneConnection);
Utilisateur unUtilisateur =
unCourtierBDUtilisateur.chercherUtilisateurParIdUtilisateur(idUtilisateur);
//Création du courtier et dématérialisation des PrêtsEnCours
CourtierBDPrêtEnCours unCourtierBDPrêtEnCours = new CourtierBDPrêtEnCours(uneConnection);
unCourtierBDPrêtEnCours.chercherLesPrêtsEnCours(unUtilisateur);
JOptionPane.showMessageDialog(null,
"Nombre de prêts en cours :" + unUtilisateur.getNbPrêtsEnCours());
…
21/04/2017
© Robert Godin. Tous droits réservés.
23
: Commis au prêt
:
ContrôleEnregistrerPrêts
: CourtierBDPrêtsEnCours
:
CourtierBDUtilisateur
:
Usin eConnection
:
CourtierBDExemplaire
1. créerNouveauPrêt()
uneConnection
2. getConnexionSansAutoCommit( )
3. «create»
4. uneConnection
5 . entrer(i dUti lisateur)
6. «cre ate»(uneConnection)
7. chercherUtilisateur(idUtilisateur)
uneTransaction
8. prepareStatement()
9. «create»(uneConnection)
10 . chercherLesPrêtsEnCours(un Uti lisateur )
11. prepareStatement()
12. chercherVariablesStatiquesDeMembre( )
13. prepareStatement()
14. entrer(i dExempl aire)
15. «create»(uneConnection)
16. chercherExem plaire (i dExempla ire )
17. prepareStatement()
18. insérerPrêtEn Cou rs (unPrêtEnCours)
19. prepapreStatement()
20. commit()
21. cl ose()
21/04/2017
© Robert Godin. Tous droits réservés.
24
15.1.1 Enjeux de conception
d'une application Java/JDBC
: Commis au prêt
:
ContrôleEnregistrerPrêts
: CourtierBDPrêtsEnCours
:
CourtierBDUtilisateur
:
Usin eConnection
:
CourtierBDExemplaire
1. créerNouveauPrêt()
uneConnection
2. getConnexionSansAutoCommit( )
3. «create»
4. uneConnection
5 . entrer(i dUti lisateur)
6. «cre ate»(uneConnection)
Dépendance
au temps de
réflexion
7. chercherUtilisateur(idUtilisateur)
uneTransaction
8. prepareStatement()
9. «create»(uneConnection)
10 . chercherLesPrêtsEnCours(un Uti lisateur )
11. prepareStatement()
12. chercherVariablesStatiquesDeMembre( )
13. prepareStatement()
14. entrer(i dExempl aire)
15. «create»(uneConnection)
16. chercherExem plaire (i dExempla ire )
17. prepareStatement()
18. insérerPrêtEn Cou rs (unPrêtEnCours)
19. prepapreStatement()
21/04/2017
© Robert Godin. Tous
droits réservés.
20. commit()
21. cl ose()
25
Limiter les verrouillages ?
Transaction T1
Transaction T2
SQL> SELECT * FROM PrêtEnCours
2 WHERE idUtilisateur = 2;
IDEXEMPLAI DATEPRÊT IDUTILISAT
---------- -------- ---------3
02-03-21 2
SQL> SELECT * FROM PrêtEnCours
2 WHERE idUtilisateur = 2;
IDEXEMPLAI DATEPRÊT IDUTILISAT
---------- -------- ---------3
02-03-21 2
SQL> INSERT INTO
2 PrêtEnCours(idExemplaire,idUtilisateur)
3 VALUES(4,2);
1 row created.
SQL> COMMIT;
Commit complete.
SQL> INSERT INTO
2 PrêtEnCours(idExemplaire,idUtilisateur)
3 VALUES(5,2);
1 row created.
SQL> COMMIT;
Commit complete.
Dépasse la limite du nombre de prêts (maximum 2)
21/04/2017
© Robert Godin. Tous droits réservés.
26
Limiter la durée des
transactions

Contraintes de sérialisabilité ?
21/04/2017
© Robert Godin. Tous droits réservés.
27
Vérification des
contraintes par le serveur
Transaction T1
Transaction T2
SQL> SELECT * FROM PrêtEnCours
2 WHERE idUtilisateur = 2;
IDEXEMPLAI DATEPRÊT IDUTILISAT
---------- -------- ---------3
02-03-21 2
SQL> SELECT * FROM PrêtEnCours
2 WHERE idUtilisateur = 2;
IDEXEMPLAI DATEPRÊT IDUTILISAT
---------- -------- ---------3
02-03-21 2
Non
sérialisabilité
perçue par T2
SQL> INSERT INTO
2 PrêtEnCours(idExemplaire,idUtilisateur)
3 VALUES(4,2);
1 row created.
SQL> COMMIT;
Commit complete.
SQL> INSERT INTO
2 PrêtEnCours(idExemplaire,idUtilisateur)
3 VALUES(5,2);
INSERT
INTO
PrêtEnCours(idExemplaire,idUtilisateur)
*
ERROR at line 1:
ORA-20101: le nombre maximal d'emprunts est
atteint
ORA-06512: at "CLERAT.BIPRÊTENCOURSVÉRIFIER", line
59
ORA-04088: error during execution of trigger
'CLERAT.BIPRÊTENCOURSVÉRIFIER'
21/04/2017
TRIGGER
© RobertUn
Godin.
Tousempêche
droits l'insertion
réservés.fautive.
28
Contrôle de concurrence optimiste par
estampillage explicite

Lecture en une première transaction
–

Note l’estampille courante (ou numéro de version, …)
Écriture en une deuxième transaction distincte
–
Vérifie si l’état a changé depuis la lecture




Si non, met à jour les données et l’estampille
Si oui, annulation
Ne bloque pas les objets lus
Populaire dans les applications Web
21/04/2017
© Robert Godin. Tous droits réservés.
29
Réduire l'interactivité
Nom: EnregistrerPrêtsSimple
Description courte : Enregistrer les prêts en un aller-retour au système
Type: Interactif
Description : Ce cas d'utilisation est déclenché par le commis au prêt suite à une requête d'un
membre ou d'un employé et lui permet d'enregistrer un prêt en un aller-retour au système.
Exigence d'intégrité : la séquence constitue une transaction
Séquence normale d'interaction :
Commis au prêt
Système
1. Le commis invoque l'écran d'enregistrement du
prêt
2. Le système demande la saisie de l'identificateur de
l'utilisateur et de l’exemplaire
3. Le commis saisit l'identificateur de l'utilisateur à
l'aide de la carte de membre ou manuellement et
entre l'identificateur de l'exemplaire à l'aide du
lecteur optique ou manuellement.
4. Le système enregistre le prêt, affiche un message
d'acquiescement.
21/04/2017
© Robert Godin. Tous droits réservés.
30
ControleEnregistrerPretsSimple
: Commis au prêt
:
ContrôleEnregistrerPrêts
:
UsineConnection
: CourtierBDPrêtsEnCours
1. créerNouveauPrêt()
uneConnection
2. getConnexion( )
4. uneConnection
3. «create»
5. entrer(i dU til isate ur, i dExempl aire)
6. «create»(uneConnection)
7. insérerPrêtEnCours (unPrêtEnCours)
8. prepapreStatem ent()
uneTransaction
9. close()
21/04/2017
© Robert Godin. Tous droits réservés.
31
Libérer la connexion
entre les appels
public typeRetour nomMéthodeCourtier(listeParamètres)
throws Exception {
Connection uneConnection = uneUsineConnection.getConnection…
// Utiliser uneConnection
…
uneConnection.close();
}
Transaction ne peut chevaucher les appels
 Ouverture/fermeture couteuse

–
21/04/2017
Solution : connection pooling (JDBC 2)
© Robert Godin. Tous droits réservés.
32
15.1.2 Gestion de la
sécurité

Qui gère l ’idUtilisateur et le mot de passe ?
–
–
21/04/2017
par les données : le SGBD
par les traitements : le programme client
© Robert Godin. Tous droits réservés.
33
ControleEnrPretsSecuriteParDonnees
package ExemplesJDBC.GererPrets;
/* Classe de contrôle simple pour EnregistrerPrêts
* Interface à l'utilisateur minimaliste
* NB Vérifie les conditions de prêt et le statut de l'exemplaire au niveau du programme
client
*/
import java.sql.*;
import javax.swing.JOptionPane;
import java.util.*;
class ControleEnrPretsSecuriteParDonnees {
public static void main (String args []) throws Exception {
String idCommis =
JOptionPane.showInputDialog("Entrez l'identificateur du commis au prêt: ");
String motDePasse =
JOptionPane.showInputDialog("Entrez le mot de passe : ");
// Création d'une Connection à partir du idCommis et de son motDePasse
UsineConnection uneUsineConnection = new UsineConnection();
Connection uneConnection = uneUsineConnection.getConnectionSansAutoCommit(
"oracle.jdbc.driver.OracleDriver",
"jdbc:oracle:thin:@localhost:1521:ora817i",
idCommis, motDePasse);
try{
// Dématérialiser l'Utilisateur et ses PrêtsEnCours
…
// Le reste du code est identique à ControleEnregistrerPrets
21/04/2017
© Robert Godin. Tous droits réservés.
34
ControleEnrPretsSecuriteParTraitement
package ExemplesJDBC.GererPrets;
/* Classe de contrôle pour EnregistrerPrêts
* Sécurité «par les traitements»
* Interface à l'utilisateur minimaliste
* NB Vérifie les conditions de prêt et le statut de l'exemplaire au niveau du programme client
*/
import java.sql.*;
import javax.swing.JOptionPane;
import java.util.*;
class ControleEnrPretsSecuriteParTraitement {
public static void main (String args []) throws Exception {
21/04/2017
// Création d'une Connection avec un authorizationID SQL indépendant du idCommis
UsineConnection uneUsineConnection = new UsineConnection();
Connection uneConnection = uneUsineConnection.getConnectionSansAutoCommit(
"oracle.jdbc.driver.OracleDriver",
"jdbc:oracle:thin:@localhost:1521:ora817i",
"clerat", "oracle");
try{
// Authentifier le commis au prêt (en utilisant la table Utilisateur)
String idCommis =
JOptionPane.showInputDialog("Entrez l'identificateur du commis au prêt: ");
CourtierBDUtilisateur unCourtierBDUtilisateur =
new CourtierBDUtilisateur(uneConnection);
Utilisateur lUtilisateur =
unCourtierBDUtilisateur.chercherUtilisateurParIdUtilisateur(idCommis);
if (!(lUtilisateur instanceof Employé)){
JOptionPane.showMessageDialog(null,"Pas un employé :"+idCommis);
}else {
Employé leCommis = (Employé)lUtilisateur;
if (!(leCommis.getCatégorieEmployé().equals("commis"))){
JOptionPane.showMessageDialog(null,"Pas un commis au prêt:"+idCommis);
} else {
String motDePasse =
JOptionPane.showInputDialog("Entrez le mot de passe : ");
if (!(leCommis.getMotPasse().equals(motDePasse))){
JOptionPane.showMessageDialog(null,"Mot de passe invalide:"+motDePasse);
}else{//OK Commis est authentifié
// Dématérialiser l'Utilisateur et ses PrêtsEnCours
…
// Le reste
code est
identique
ControleEnregistrerPrets
© du
Robert
Godin.
Tous àdroits
réservés.
35
15.1.3 Façade pour les services
de la couche application
ControleEnregistrerPretsAvecFacade
FacadeEnregistrerPrets
Personne
Utili sateur
Membre
Connection
21/04/2017
UsineConnection
CourtierBDUtilisateur
Prêt
PrêtEnCours
CourtierBDPrêtsEnCours
© Robert Godin. Tous droits réservés.
Exemplaire
CourtierBDExemplaire
36
Diagramme de séquence pour
ControleEnregistrerPretAvecFacade
: ControleEnregistrerPretsAvecFacade
: FacadeEnregistrerPrets
: UsineConnection
uneConnection
1. «create»
1.1. getConnexionSan sAuto Commit( )
1.1.1. «create»
2. u neC onnectio n
3. chercherOTDUtilisateurPrets()
4. getStatutExemplaire()
5. insererPretEnC ours( )
6. confirmerTransaction( )
7. commit()
Dépendance réduite
par rapport à la
couche persistence
21/04/2017
8. close()
© Robert Godin. Tous droits réservés.
37
Patron d'objet de transfert
de données (OTD)
uneClasseControle
unOTD
uneClasseFacade
1. getOTD()
2. unOTD
3. getPropriété1()
4. getPropriété2()
5. getPropriété3()
etc.
21/04/2017
© Robert Godin. Tous droits réservés.
38
ControleEnregistrerPretsSimpleAvecFacade
:ControleEnregistrerPretsSimpleAvecFacade
:FacadeEnregistrerPretsSimple
: Usi neC onne cti on
uneConnection
1. «create»
2. insererPretEnCours(idUtilisateur,idExemplaire)
3. getConn ection()
4. «create»
5. une Con nection
6. cl ose()
Connexion libérée
entre les appels
21/04/2017
© Robert Godin. Tous droits réservés.
39
15.2 Développement d'applications
Web par servlet Java
Fureteur Web :
- présentation
Client mince
21/04/2017
Serveur d'application :
-contrôle
-domaine d'application
SGBD
- persistence
- partie du domaine d'application ?
Serveur d'application
© Robert Godin. Tous droits réservés.
Serveur BD
40
15.2.1 Principe de base du Web : le
protocole HTTP et le langage de
présentation HTML
Requête HTTP
Fureteur Web
Serveur Web
Réponse HTTP
21/04/2017
© Robert Godin. Tous droits réservés.
41
15.2.2 Développement
de servlet Java
Fureteur Web
FORM HTML
Serveur d'application (Web)
1. Le fureteur Invoque l'écran
de saisie (HTTP GET)
2. Le serveur télécharge la
FORM HTML
3. Le fureteur envoie des
paramètres (HTTP POST)
4. Le serveur délègue l'appel
au conteneur à servlet
Conteneur à servlet
5. Le conteneur à servlet
démarre un fil de la servlet
servlet
Java
Serveur BD
6. La servelt fait appel au
serveur de BD
BD
7. La servelt produit la
réponse HTTP qui est
retournée au fureteur
21/04/2017
© Robert Godin. Tous droits réservés.
42
Diagramme de séquence de
l'invocation de la servlet
ServletEnregistrerPretSimpleFacade
unFureteur
unServeurWeb
1. requête HTTP POST
: Facade En registrerPretSimple
: ServletEnregistrerPretSimpleFacade
2. doPost(HttpServletRequest, H ttpServl etRe sponse)
3. insererPretEnCours(idUtilisateur,idExemplaire )
4. date du prêt
5. HttpServle tRespon se
6. réponse HTTP (document HTML)
Réutilisation des classes du client-serveur
21/04/2017
© Robert Godin. Tous droits réservés.
43
<html>
<head>
<title>
Bibliothèque LeRat : Enregistrer un prêt (avec un servlet, interaction simplifiée)
</title>
</head>
<body>
<form action = "servlet/ServletEnregistrerPretsSimpleFacade" method = "post">
<p> Entrez l'identificateur de l'utilisateur et de l'exemplaire et cliquez Soumettre</p>
<table>
<tr>
<td>Identificateur d'utilisateur</td>
<td><input type = "text" name = "idUtilisateur" /></td>
</tr>
<tr>
<td>Identificateur d'exemplaire</td>
<td><input type = "text" name = "idExemplaire" /></td>
</tr>
</table>
<input type = "submit" value = "Soumettre" />
</form>
</body>
</html>
21/04/2017
© Robert Godin. Tous droits réservés.
44
import
import
import
import
import
javax.servlet.*;
javax.servlet.http.*;
java.io.*;
java.util.*;
java.sql.*;
public class ServletEnregistrerPretsSimpleFacade extends HttpServlet {
public
void
doPost(HttpServletRequest
request,
HttpServletResponse
response)
ServletException, IOException{
//Chercher le paramètre idUtilisateur de la FORM HTML et créer un Utilisateur
String idUtilisateur = "";
try { idUtilisateur = request.getParameter("idUtilisateur"); }
catch (Exception e) { e.printStackTrace(); }
throws
//Chercher le paramètre idExemplaire de la FORM HTML et créer un Exemplaire
String idExemplaire = "";
try { idExemplaire = request.getParameter("idExemplaire"); }
catch (Exception e) { e.printStackTrace(); }
// Entête de la page de réponse HTML
response.setContentType("text/html");
OutputStreamWriter unOutputStreamWriter =
new OutputStreamWriter(response.getOutputStream());
PrintWriter out = new PrintWriter(unOutputStreamWriter);
out.println("<html>");
out.println("<head><title>Réponse de ServletEnregistrerPretsSimpleFacadeEJB</title></head>");
out.println("<body>");
FacadeEnregistrerPretsSimple uneFacadeEnregistrerPretsSimple = null;
try{
uneFacadeEnregistrerPretsSimple = new FacadeEnregistrerPretsSimple();
// Enregistrer le prêt en passant par la façade
java.sql.Date datePret =
uneFacadeEnregistrerPretsSimple.insererPretEnCours(idUtilisateur,idExemplaire);
// Message de confirmation du prêt
out.println("Prêt de l'exemplaire " + idExemplaire +
" à l'utilisateur " + idUtilisateur + " confirmé.<br>Date :" + datePret);
}
…
21/04/2017
© Robert Godin. Tous droits réservés.
45
15.2.3 Cycle de vie
d'une servlet

Compilation et démarrage à la première
invocation (par conteneur)
–
init() appelé une fois
 permet
–
–
nouveau fil à chaque invocation d ’une méthode
de service (doGet, doPost,…)
destroy() à la fin
 e.g.
21/04/2017
d ’initialiser des ressources (e.g. connexion)
fermer connexion
© Robert Godin. Tous droits réservés.
46
15.2.4 Gestion d'une session
HTTP dans une servlet
La FORM
n ’envoie que
le idUtilisateur
21/04/2017
© Robert Godin. Tous droits réservés.
47
FormIdentificationUtilisateurFacade.html
<html>
<head>
<title> Exemple de session http pour cas Enregistrer Prêts : Identification utilisateur
</title>
</head>
<body>
<form action = "servlet/ServletIdentificationUtilisateurFacade" method = "post">
<p>Entrez l'identifiant de l'utilisateur et cliquez Soumettre</p>
<input type = "text" name = "idUtilisateur" />
<input type = "submit" value = "Soumettre" />
</form>
</body>
</html>
21/04/2017
© Robert Godin. Tous droits réservés.
48
EnregistrerPrets en deux servlets
unFureteur
unServeurWeb
: ServletIdentificationUtilisateurFacade
1. requête HTTP POST pour ServletIdentificationUtilisateurFacade
uneSession :
HttpSession
2. doPost(HttpServletRequest, HttpServletResponse)
3. «create»
4. « crea te»
uneFacadeEnregistrerPrets :
FacadeEnregistrerPrets
5. chercherOTDUtilisateurPrets()
uneTransaction
6. setAttribute('uneFacadeEnregistrerPrets', uneFacadeEnregistrerPrets)
7. setAttribute('i dU til is ateu r',i dUtil is ateur)
8. HttpServletResponse
: ServletTerminerPretFacade
9. réponse HTTP (FormTerminerPret)
10. requête HTTP POST pour ServletTerminerPretFacade
11. doPost(HttpServletRequest, HttpServletResponse)
1 2. getAttribute('un eFacadeEn regi strerPrets')
1 3. getAttribute('idU til isa teur')
1 4. getStatutExem plaire()
15. insererPretEnCo urs(id Utilisateur,idExempla ire )
16 . confirm erTransacti on( )
17. HttpServletResponse
18. réponse HTTP (confirmation)
21/04/2017
© Robert Godin. Tous droits réservés.
49
…
public class ServletIdentificationUtilisateurFacade extends HttpServlet {
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//Chercher le paramètre idUtilisateur de la form
String idUtilisateur = "";
try { idUtilisateur = request.getParameter("idUtilisateur"); }
catch (Exception e) { e.printStackTrace(); }
// Créer un objet session si non existant
HttpSession uneSession = request.getSession(true);
// Entête de la page de réponse html
response.setContentType("text/html");
…
try{
// Créer la facade et chercher l'OTDUtilisateurPrets
FacadeEnregistrerPrets uneFacadeEnregistrerPrets = new FacadeEnregistrerPrets();
OTDUtilisateurPrets unOTDUtilisateurPrets =
uneFacadeEnregistrerPrets.chercherOTDUtilisateurPrets(idUtilisateur);
// Affichage de l'idUtilisateur et du nombre de prêts
out.println("Utilisateur :" + idUtilisateur);
out.println(" Nombre de prêts en cours :" + unOTDUtilisateurPrets.getNbPretsEnCours()+"<br>");
// Vérifier si les conditions préalables sont violées
…
}else{
// Sauvegarder les objets du contexte de la session
uneSession.setAttribute("uneFacadeEnregistrerPrets", uneFacadeEnregistrerPrets);
uneSession.setAttribute("idUtilisateur", idUtilisateur);
// Construire la FORM pour saisir l'idExemplaire
out.println("<form action = \"ServletTerminerPretFacade\" method = \"post\">");
out.println("<p>Entrez l'identifiant de l'exemplaire et cliquez Soumettre</p>");
out.println("<input type = \"text\" name = \"idExemplaire\" />");
out.println("<input type = \"submit\" value = \"Soumettre\" />");
out.println("</form>");
}
...
21/04/2017
© Robert Godin. Tous droits réservés.
50
…
public class ServletTerminerPretFacade extends HttpServlet {
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//Chercher le paramètre idUtilisateur de la form
String idExemplaire = "";
try { idExemplaire = request.getParameter("idExemplaire"); }
catch (Exception e) { e.printStackTrace(); }
// Chercher l'objet de la session en cours
HttpSession uneSession = request.getSession(false);
// Chercher les objets du contexte de la session en cours
FacadeEnregistrerPrets uneFacadeEnregistrerPrets =
(FacadeEnregistrerPrets)uneSession.getAttribute("uneFacadeEnregistrerPrets");
String idUtilisateur = (String)uneSession.getAttribute("idUtilisateur");
// Entête de la page de réponse html
…
try{
// Vérifier statut de l'exemplaire
String statut = uneFacadeEnregistrerPrets.getStatutExemplaire(idExemplaire);
if (statut.equals("disponible")){
// Enregistrer le prêt en passant par la façade
java.sql.Date datePret =
uneFacadeEnregistrerPrets.insererPretEnCours();
…
finally{
try{
out.println("</body></html>");
out.close();
uneFacadeEnregistrerPrets.confirmerTransaction();
}
catch(Exception lException){
lException.printStackTrace();
}
}
}
}
21/04/2017
© Robert Godin. Tous droits réservés.
51
15.2.5 DataSource,
ConnectionPoolDataSource et
PooledConnection

Passage par serveur JNDI (Java Naming and Directory Interface)
–
–
–
21/04/2017
e.g. service d’annuaire LDAP
repérage de ressources (e.g. serveur BD)
accès sécurisé
© Robert Godin. Tous droits réservés.
52
Ouverture d ’une Connection
en passant par DataSource
package ExemplesJDBC.GererPrets;
import java.sql.*;
import javax.naming.*;
import javax.sql.*;
public class UsineConnection {
public UsineConnection() {}
…
public Connection getConnectionFromDataSource(
String nomDataSource)
throws Exception {
InitialContext unContexte = new InitialContext();
DataSource unDataSource =
(DataSource)unContexte.lookup(nomDataSource);
Connection uneConnection = unDataSource.getConnection();
return uneConnection;
}
}
21/04/2017
© Robert Godin. Tous droits réservés.
53
Enregistrement du DataSource par descripteur de
déploiement XML pour serveur embarqué OC4J de
Oracle9i JDevelopper (généré par JDevelopper)
<?xml version = '1.0' encoding = 'windows-1252'?>
<!DOCTYPE data-sources PUBLIC "Orion data-sources" "http://xmlns.oracle.com/ias/dtds/data-sources.dtd">
<data-sources>
<data-source class="com.evermind.sql.DriverManagerDataSource"
connection-driver="oracle.jdbc.driver.OracleDriver"
ejb-location="jdbc/ora9icleratDS"
inactivity-timeout="30"
location="jdbc/ora9icleratCoreDS"
name="ora9icleratDS"
password="oracle"
pooled-location="jdbc/ora9icleratPooledDS" url="jdbc:oracle:thin:@localhost:1521:ora9i"
username="clerat"
xa-location="jdbc/xa/ora9icleratXADS"/>
</data-sources>
Connection uneConnection =
uneUsineConnection.getConnectionFromDataSource("jdbc/ora9icleratCoreDS");
21/04/2017
© Robert Godin. Tous droits réservés.
54
Partage d'un pool de connexions
package ExemplesJDBC.GererPrets;
import java.sql.*;
import javax.naming.*;
import javax.sql.*;
import oracle.jdbc.pool.*;
public class UsineConnection {
public UsineConnection() {}
…
public Connection getConnectionFromConnectionPoolDataSource(
String nomDataSource)
throws Exception {
InitialContext unContexte = new InitialContext();
ConnectionPoolDataSource unDataSource =
(ConnectionPoolDataSource)unContexte.lookup(nomDataSource);
PooledConnection unePooledConnection = unDataSource.getPooledConnection();
Connection uneConnection = unePooledConnection.getConnection();
return uneConnection;
}
}
21/04/2017
© Robert Godin. Tous droits réservés.
55
15.2.6 Mécanismes de sécurité
pour l'accès à un servlet
Sécurité programmatique
 Sécurité déclarative par le conteneur

–
–
21/04/2017
rôles/utilisateurs
géré par conteneur
© Robert Godin. Tous droits réservés.
56
15.3 Architecture pour la
persistance transparente



Persistance gérée par la classe métier
Transparent au client
Mécanisme paresseux de matérialisation/dématérialisation
–
–

Norme Java Data Objects (JDO)
–
–
–

gestion de cache transactionnelle
programmation complexe
http://access1.sun.com/jdo/
inspirée de ODMG
outils : http://www.javaskyline.com/database.html
Nouvelle norme Java Persistence API (JPA)
–
21/04/2017
Inspirée des meilleures solutions (JDO, Hibernate, Toplink, …)
© Robert Godin. Tous droits réservés.
57
15.4 Norme EJB de J2EE
(http://java.sun.com/products/ejb/)

Transparence « extrême »
–
–
–

persistance
répartition
services
Spécification déclarative de services
–
–
–
comportement transactionnel
sécurité
gestion de ressources (CPU, mémoire, serveurs, ...)



e.g. partage transparent d’un bassin d’objets EJB (pooling) entre clients
robustesse, « scalabilité »
Prise en charge des services par le conteneur à EJB
21/04/2017
© Robert Godin. Tous droits réservés.
58
Séparation propre des
responsabilités (rôles)

Fournisseur de EJB (EnterpriseBean Provider)
–

Assembleur d'application (Application Assembler) .
–

serveur EJB : services de bas niveau pour le conteneur EJB
Fournisseur de conteneur EJB (EJB Container Provider)
–

déploie les EJB dans leur environnement d'exécution
Fournisseur de serveur EJB (EJB Server Provider).
–

assemble EJB + servlet, JSP, HTML, etc.
Déployeur (Deployer)
–

développe les composantes EJB
conteneur EJB : environnement de déploiement et d'exécution des EJB
Administrateur de Système (System Administrator)
–
21/04/2017
administre serveur et le conteneur EJB
© Robert Godin. Tous droits réservés.
59
Catégories de EJB

EJB session (Session Bean)
–
–
–
–
service d ’objet distant
un seul client à la fois par objet
partage d’un bassin d’objets entre clients
état au besoin entre les appels

–

non persistent (perdu suite à une panne)
EJB entité (Entity Bean)
–
–
–

stateful/stateless session EJB
services d'objet distant persistant
objet ~ proxy pour une ligne d'une table
un objet EJB entité peut être partagé par plusieurs clients
EJB message (Message Bean)
–
21/04/2017
service de message
© Robert Godin. Tous droits réservés.
60
Codage d ’un EJB
 Interface
–
usine à objet EJB
 Interface
–
21/04/2017
à distance (remote)
accès aux méthodes d'un objet EJB
 Classe
–
home
bean (bean class)
implémente les méthodes de l'objet EJB
© Robert Godin. Tous droits réservés.
61
15.4.1 Développement d'un EJB session sans
état : FacadeEnrPretSimpleSessionEJB

Interface home (usine à EJB)
package ExemplesJDBC.GererPrets;
import javax.ejb.EJBHome;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
public interface FacadeEnrPretSimpleSessionEJBHome extends EJBHome
{
FacadeEnrPretSimpleSessionEJB create() throws RemoteException, CreateException;
}

Interface remote (interface à l’objet EJB)
package ExemplesJDBC.GererPrets;
import javax.ejb.EJBObject;
import java.rmi.RemoteException;
import java.sql.Date;
public interface FacadeEnrPretSimpleSessionEJB extends EJBObject
{
Date insererPretEnCours(String idUtilisateur, String idExemplaire) throws RemoteException,
Exception;
}
NB Paramètres sérialisables (au sens de Java) passés par valeur
21/04/2017
© Robert Godin. Tous droits réservés.
62
package ExemplesJDBC.GererPrets.impl;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
import java.sql.Date;
import ExemplesJDBC.GererPrets.*;
import java.sql.Connection;
import java.util.*;
public class FacadeEnrPretSimpleSessionEJBBean implements SessionBean
{
public void ejbCreate(){}
public void ejbActivate(){}
public void ejbPassivate(){}
public void ejbRemove(){}
public void setSessionContext(SessionContext ctx){}
public Date insererPretEnCours(String idUtilisateur, String idExemplaire) throws Exception
{
UsineConnection uneUsineConnection = new UsineConnection();
Connection uneConnection =
uneUsineConnection.getConnectionFromDataSource("jdbc/ora9icleratCoreDS");
Utilisateur unUtilisateur = new Utilisateur(idUtilisateur);
Exemplaire unExemplaire = new Exemplaire(idExemplaire);
// Générer la date du jour et l'objet PrêtEnCours
Calendar maintenant = Calendar.getInstance(); // Calendrier avec date actuelle
java.sql.Date dateMaintenant = new java.sql.Date(maintenant.getTime().getTime());
PrêtEnCours leNouveauPrêtEnCours =
new PrêtEnCours(unUtilisateur,dateMaintenant,unExemplaire);
// Matérialiser le PrêtEnCours dans la BD
CourtierBDPrêtEnCours unCourtierBDPrêtEnCours =
new CourtierBDPrêtEnCours(uneConnection);
unCourtierBDPrêtEnCours.insérerPrêtEnCours(leNouveauPrêtEnCours);
// Pas besoin de commit
uneConnection.close();
return dateMaintenant;
}
}
21/04/2017
© Robert Godin. Tous droits réservés.
63
Descripteur de
déploiement XML
<?xml version = '1.0' encoding = 'windows-1252'?>
<!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 1.1//EN"
"http://java.sun.com/j2ee/dtds/ejb-jar_1_1.dtd">
<ejb-jar>
<enterprise-beans>
<session>
<description>Session Bean ( Stateless )</description>
<display-name>FacadeEnrPretSimpleSessionEJB</display-name>
<ejb-name>FacadeEnrPretSimpleSessionEJB</ejb-name>
<home>ExemplesJDBC.GererPrets.FacadeEnrPretSimpleSessionEJBHome</home>
<remote>ExemplesJDBC.GererPrets.FacadeEnrPretSimpleSessionEJB</remote>
<ejb-class>ExemplesJDBC.GererPrets.impl.FacadeEnrPretSimpleSessionEJBBean</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Bean</transaction-type>
</session>
</ejb-jar>
21/04/2017
© Robert Godin. Tous droits réservés.
64
Client du EJB : façade locale qui
délègue à la façade EJB distante
(patron Business Delegate)
package ExemplesJDBC.GererPrets;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.InitialContext;
import ExemplesJDBC.GererPrets.FacadeEnrPretSimpleSessionEJB;
import ExemplesJDBC.GererPrets.FacadeEnrPretSimpleSessionEJBHome;
// Façade pour le cas d'utilisation EnregistrerPretsSimple
// La façade utilise les services du EJB session FacadeEnrPretSimpleSessionEJB
public class FacadeEJBEnregistrerPretsSimple
{
public FacadeEJBEnregistrerPretsSimple()throws Exception
{
}
// Insère le prêt et retourne la date d'enregistrement
public java.sql.Date insererPretEnCours(
String idUtilisateur, String idExemplaire) throws Exception{
// Chercher une référence au EJB session
FacadeEnrPretSimpleSessionEJB uneFacadeEnrPretSimpleSessionEJB;
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.evermind.server.rmi.RMIInitialContextFactory");
env.put(Context.SECURITY_PRINCIPAL, "admin");
env.put(Context.SECURITY_CREDENTIALS, "welcome");
env.put(Context.PROVIDER_URL, "ormi://localhost:23893/current-workspace-app");
Context ctx = new InitialContext(env);
FacadeEnrPretSimpleSessionEJBHome facadeEnrPretSimpleSessionEJBHome =
(FacadeEnrPretSimpleSessionEJBHome)ctx.lookup("FacadeEnrPretSimpleSessionEJB");
uneFacadeEnrPretSimpleSessionEJB = facadeEnrPretSimpleSessionEJBHome.create();
// Déléguer l'insertion du PrêtEnCours à la méthode du EJB
return uneFacadeEnrPretSimpleSessionEJB.insererPretEnCours(idUtilisateur,idExemplaire);
}
}
21/04/2017
© Robert Godin. Tous droits réservés.
65
JDevelopper 10g
(déploiement de défaut différent)
// Chercher une référence au EJB session
Context context = new InitialContext();
FacadeEnrPretSimpleSessionEJBHome facadeEnrPretSimpleSessionEJBHome =
(FacadeEnrPretSimpleSessionEJBHome)PortableRemoteObject.narrow(
context.lookup("FacadeEnrPretSimpleSessionEJB"),
FacadeEnrPretSimpleSessionEJBHome.class);
FacadeEnrPretSimpleSessionEJB uneFacadeEnrPretSimpleSessionEJB;
uneFacadeEnrPretSimpleSessionEJB = facadeEnrPretSimpleSessionEJBHome.create();
21/04/2017
© Robert Godin. Tous droits réservés.
66
Interposition transparente
d ’objets
Conteneur
de bean
Client du EJB
: Con trole Enregi strerPretsSi mpl eAvecFacadeEJB
: FacadeEJBEnregistrerPretsSimple
Ob jet home
In terpositi on transparente d'o bjets
générés par le conten eur pour e ffectuer
l'envoie de message à l'obj et be an
1. insererPretEnCours(id Utilisateur,idExemplaire)
ObjetEJB local qui implémente
l'interface remote
ctx : Context
2. «create»
3. lookup()
facadeEnrPretSimpl
eSessionEJBHome
4. facadeEnrPretSimpleSessionEJBHome
5. create()
Objet bean
uneFacad eEnrPretSim ple SessionEJB :
FacadeEnregistrerPretsSimpleSessionEJB
6. uneFacadeEnrPretSimpleSessionEJB
: FacadeEn regi strerPretsSimpl eSessi onEJBBean
7 . in sererPretEnCours(idUtil is ateu r,idExemp laire )
8. insererPretEnCou rs(idUtilisateur,idExemplaire)
Poste du client du EJB
21/04/2017
© Robert Godin. Tous droits réservés.
Conteneur EJB
67
Dans ServletEnregistrerPretsSimpleFacade remplacer
FacadeEnregistrerPretsSimple par une
FacadeEJBEnregitrerPretSimple
package ExemplesJDBC.GererPrets;
import
import
import
import
import
javax.servlet.*;
javax.servlet.http.*;
java.io.*;
java.util.*;
java.sql.*;
public class ServletEnregistrerPretsSimpleFacadeEJB extends HttpServlet {
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException{
…
FacadeEJBEnregistrerPretsSimple uneFacadeEnregistrerPretsSimple = null;
try{
uneFacadeEnregistrerPretsSimple = new FacadeEJBEnregistrerPretsSimple();
…
}
}
21/04/2017
© Robert Godin. Tous droits réservés.
68
15.4.2 Développement d'un
EJB session avec état
package ExemplesJDBC.GererPrets;
import javax.ejb.EJBHome;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
public interface FacadeEnrPretsSessionEJBHome extends EJBHome
{
FacadeEnrPretsSessionEJB create() throws RemoteException, CreateException;
}
package ExemplesJDBC.GererPrets;
import javax.ejb.EJBObject;
import java.rmi.RemoteException;
import java.sql.Date;
public interface FacadeEnrPretsSessionEJB extends EJBObject
{
OTDUtilisateurPrets chercherOTDUtilisateurPrets(String idUtilisateur)
throws Exception, RemoteException;
void confirmerTransaction() throws Exception, RemoteException;
String getStatutExemplaire(String idExemplaire) throws Exception, RemoteException;
Date insererPretEnCours() throws Exception, RemoteException;
}
21/04/2017
© Robert Godin. Tous droits réservés.
69
Classe bean
package ExemplesJDBC.GererPrets.impl;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
import java.sql.Connection;
import ExemplesJDBC.GererPrets.*;
import java.sql.Date;
import java.util.*;
État maintenu
entre les appels
public class FacadeEnrPretsSessionEJBBean implements SessionBean
{
private Connection uneConnection;
private Utilisateur unUtilisateur;
private Exemplaire unExemplaire;
private CourtierBDPrêtEnCours unCourtierBDPretEnCours;
public void ejbCreate()throws Exception{}
public void ejbActivate(){}
public void ejbPassivate(){}
public void ejbRemove(){}
Méthode du cycle de vie
appelée à la création de
l’objet avant ejbCreate()
public void setSessionContext(SessionContext ctx) {
try {
UsineConnection uneUsineConnection = new UsineConnection();
uneConnection =
uneUsineConnection.getConnectionFromDataSourceSansAutoCommit("jdbc/ora9icleratCoreDS");
}
catch(Exception lException){
lException.printStackTrace();
}
}
NB setSessionContext() permet
d’initialiser la connexion au
DataSource (interdit dans
ejbCreate()…)
…
21/04/2017
© Robert Godin. Tous droits réservés.
70
Classe Bean (suite) : méthode métier
identique à FacadeEnregistrerPrets
public OTDUtilisateurPrets chercherOTDUtilisateurPrets(String idUtilisateur) throws Exception
{
// Création du courtier et dématérialisation de l'Utilisateur
CourtierBDUtilisateur unCourtierBDUtilisateur = new CourtierBDUtilisateur(uneConnection);
unUtilisateur =
unCourtierBDUtilisateur.chercherUtilisateurParIdUtilisateur(idUtilisateur);
//Création du courtier et dématérialisation des PrêtsEnCours
unCourtierBDPretEnCours = new CourtierBDPrêtEnCours(uneConnection);
unCourtierBDPretEnCours.chercherLesPrêtsEnCours(unUtilisateur);
// Retourner l'objet de transfert de données OTDUtilisateurPrets
if (unUtilisateur instanceof Membre){
unCourtierBDUtilisateur.chercherVariablesStatiquesDeMembre();
Membre unMembre = (Membre)unUtilisateur;
return new OTDUtilisateurPrets(
unMembre.getCatégorie(),
unMembre.conditionsPrêtAcceptées(),
unMembre.getNbPrêtsEnCours(),
Membre.getNbMaxPrêts(),
unMembre.getNbRetards());
} else {
return new OTDUtilisateurPrets(
unUtilisateur.getCatégorie(),true,unUtilisateur.getNbPrêtsEnCours(),0,0);
}
}
21/04/2017
© Robert Godin. Tous droits réservés.
71
Processus de création
d ’un EJB avec état
unClientEJB
unEJBHome
unObjetEJBl oca l
u nSessio nContext
unObjetEJBBean
1. create()
2. «create»
3. «create»
4. «create»
5. setSessionContext()
6. ejbCreate()
21/04/2017
© Robert Godin. Tous droits réservés.
72
Client du EJB : façade locale qui
délègue à la façade EJB distante
…
public class FacadeEJBEnregistrerPrets
{
private FacadeEnrPretsSessionEJB uneFacadeEnrPretsSessionEJB;
public FacadeEJBEnregistrerPrets()throws Exception
{
// Le constructeur crée l'objet session EJB façade
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,"com.evermind.server.rmi.RMIInitialContextFactory");
env.put(Context.SECURITY_PRINCIPAL, "admin");
env.put(Context.SECURITY_CREDENTIALS, "welcome");
env.put(Context.PROVIDER_URL, "ormi://localhost:23893/current-workspace-app");
Context ctx = new InitialContext(env);
FacadeEnrPretsSessionEJBHome facadeEnrPretsSessionEJBHome =
(FacadeEnrPretsSessionEJBHome)ctx.lookup("FacadeEnrPretsSessionEJB");
uneFacadeEnrPretsSessionEJB = facadeEnrPretsSessionEJBHome.create(
);
}
// Cherche un Utilisateur avec ses PretsEnCours et retourne
// les données nécessaires pour EnregistrerPrets (OTDUtilisateurPrets)
// Une exception est levée si l'Utilisateur n'existe pas
public OTDUtilisateurPrets chercherOTDUtilisateurPrets(String idUtilisateur)throws Exception {
return uneFacadeEnrPretsSessionEJB.
chercherOTDUtilisateurPrets(idUtilisateur);
}
…
21/04/2017
© Robert Godin. Tous droits réservés.
73
Dans ServletIdentificationUtilisateurFacade et
ServletTerminerPretFacade
remplacer FacadeEnregistrerPrets par une
FacadeEJBEnregistrerPrets
package ExemplesJDBC.GererPrets;
import
import
import
import
import
javax.servlet.*;
javax.servlet.http.*;
java.io.*;
java.util.*;
java.sql.*;
public class ServletIdentUtilFacadeEJB extends HttpServlet {
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
…
try{
// Créer la facade et chercher l'OTDUtilisateurPrets
FacadeEJBEnregistrerPrets uneFacadeEnregistrerPrets = new FacadeEJBEnregistrerPrets();
OTDUtilisateurPrets unOTDUtilisateurPrets =
uneFacadeEnregistrerPrets.chercherOTDUtilisateurPrets(idUtilisateur);
…
21/04/2017
© Robert Godin. Tous droits réservés.
74
15.4.3 Support de
transactions distribuées

Plusieurs ressources indépendantes
–
serveur BD, serveur JMS, ...
Protocole transparent de transaction répartie
 Démarcation programmatique ou déclarative

21/04/2017
© Robert Godin. Tous droits réservés.
75
15.4.4 Démarcation programmatique
de transaction distribuée (bean
managed transaction demarcation)
CREATE TABLE Compte
(noCompte
INTEGER PRIMARY KEY,
solde
DECIMAL(10,2) CHECK (solde >= 0),
dateOuverture
DATE,
noClient)
package mypackage1;
import javax.ejb.*;
import java.rmi.RemoteException;
import java.sql.Connection;
public interface BanqueSessionBMTDEJB extends EJBObject {
void transferer(int noCompteADebiter, int noCompteACrediter, double montant)
throws RemoteException, EJBException;
}
Deux connexions
indépendantes !
21/04/2017
© Robert Godin. Tous droits réservés.
76
Classe bean
…
// Exemple de EJB session qui réalise une transaction répartie
// Démarcation programmatique de transaction avec javax.transation.UserTransaction
public class BanqueSessionBMTDEJBBean implements SessionBean {
private SessionContext sessionContext;
private Connection connectionBanqueDebit;
private Connection connectionBanqueCredit;
…
public void setSessionContext(SessionContext ctx){
sessionContext = ctx;
ouvrirConnections();
}
Deux connexions
void ouvrirConnections() throws EJBException {
try{
UsineConnection uneUsineConnection = new UsineConnection();
connectionBanqueDebit =
uneUsineConnection.getConnectionFromDataSource("jdbc/ora9igodinCoreDS");
connectionBanqueCredit =
uneUsineConnection.getConnectionFromDataSource("jdbc/ora9igodin2CoreDS");
}
catch(Exception lException){
throw new EJBException(lException);
}
}
…
21/04/2017
© Robert Godin. Tous droits réservés.
77
Classe bean
public void transferer(int noCompteADebiter, int noCompteACrediter, double montant)
throws EJBException
{
try{
// Chercher le UserTransaction du sessionContext
UserTransaction uneUserTransaction = sessionContext.getUserTransaction();
// Début de la transaction répartie
uneUserTransaction.begin();
// Débiter le compte de connectionBanqueDebit
PreparedStatement preparedStatementDebit = connectionBanqueDebit.prepareStatement
("UPDATE Compte SET solde = solde - ? WHERE noCompte = ?");
preparedStatementDebit.setDouble(1,montant);
preparedStatementDebit.setInt(2,noCompteADebiter);
int n = preparedStatementDebit.executeUpdate();
preparedStatementDebit.close();
if (n != 1){throw new EJBException("Retrait a échoué");}
// Créditer le compte de connectionBanqueCredit
PreparedStatement preparedStatementCredit = connectionBanqueCredit.prepareStatement
("UPDATE Compte SET solde = solde + ? WHERE noCompte = ?");
preparedStatementCredit.setDouble(1,montant);
preparedStatementCredit.setInt(2,noCompteACrediter);
n = preparedStatementCredit.executeUpdate();
if (n != 1){throw new EJBException("Dépôt a échoué");}
preparedStatementCredit.close();
// Confirmer la transation répartie
uneUserTransaction.commit();
21/04/2017
© Robert Godin. Tous droits réservés.
78
Descripteur de
déploiement XML
<?xml version = '1.0' encoding = 'windows-1252'?>
<!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Enterprise
"http://java.sun.com/j2ee/dtds/ejb-jar_1_1.dtd">
<ejb-jar>
<enterprise-beans>
<session>
<description>Session Bean ( Stateful )</description>
<display-name>BanqueSessionBMTDEJB</display-name>
<ejb-name>BanqueSessionBMTDEJB</ejb-name>
<home>mypackage1.BanqueSessionBMTDEJBHome</home>
<remote>mypackage1.BanqueSessionBMTDEJB</remote>
<ejb-class>mypackage1.impl.BanqueSessionBMTDEJBBean</ejb-class>
<session-type>Stateful</session-type>
<transaction-type>Bean</transaction-type>
</session>
</enterprise-beans>
</ejb-jar>
21/04/2017
© Robert Godin. Tous droits réservés.
JavaBeans
1.1//EN"
79
15.4.5 Démarcation déclarative de
transaction (container managed
transaction demarcation)
<?xml version = '1.0' encoding = 'windows-1252'?>
<!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Enterprise
"http://java.sun.com/j2ee/dtds/ejb-jar_1_1.dtd">
<ejb-jar>
<enterprise-beans>
<session>
<description>Session Bean ( Stateless )</description>
<display-name>BanqueSessionCMTDEJB</display-name>
<ejb-name>BanqueSessionCMTDEJB</ejb-name>
<home>mypackage1.BanqueSessionCMTDEJBHome</home>
<remote>mypackage1.BanqueSessionCMTDEJB</remote>
<ejb-class>mypackage1.impl.BanqueSessionCMTDEJBBean</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Container</transaction-type>
</session>
</enterprise-beans>
<assembly-descriptor>
<container-transaction>
<method>
<ejb-name>BanqueSessionCMTDEJB</ejb-name>
<method-name>tranferer</method-name>
<method-params>
<method-param>int</method-param>
<method-param>int</method-param>
<method-param>double</method-param>
</method-params>
</method>
<trans-attribute>RequiresNew</trans-attribute>
</container-transaction>
</assembly-descriptor>
</ejb-jar>
21/04/2017
© Robert Godin. Tous droits réservés.
JavaBeans
1.1//EN"
80
Classe bean
// Attribut de transaction RequiresNew dans le descripteur de déploiement XML
public void transferer(int noCompteADebiter, int noCompteACrediter, double montant)
throws EJBException{
try{
// Débiter le compte de connectionBanqueDebit
PreparedStatement preparedStatementDebit = connectionBanqueDebit.prepareStatement
("UPDATE Compte SET solde = solde - ? WHERE noCompte = ?");
preparedStatementDebit.setDouble(1,montant);
preparedStatementDebit.setInt(2,noCompteADebiter);
int n = preparedStatementDebit.executeUpdate();
preparedStatementDebit.close();
if (n != 1){throw new EJBException("Retrait a échoué");}
// Créditer le compte de connectionBanqueCredit
PreparedStatement preparedStatementCredit = connectionBanqueCredit.prepareStatement
("UPDATE Compte SET solde = solde + ? WHERE noCompte = ?");
preparedStatementCredit.setDouble(1,montant);
preparedStatementCredit.setInt(2,noCompteACrediter);
n = preparedStatementCredit.executeUpdate();
if (n != 1){throw new EJBException("Dépôt a échoué");}
preparedStatementCredit.close();
}
catch(Exception lException){
throw new EJBException(lException);
}
}
21/04/2017
© Robert Godin. Tous droits réservés.
81
balise <trans-attribut>
 NotSupported
 Required
 Supports
 RequiresNew
 Mandatory
 Never
21/04/2017
© Robert Godin. Tous droits réservés.
82
15.4.6 EJB entité

Bean Managed Persistence - BMP
–

codage du SQL/JDBC
Container Managed Persistence - CMP
–
–
–
–
21/04/2017
SQL généré par conteneur
associations depuis EJB 2
héritage avec EJB3
clé primaire obligatoire ...
© Robert Godin. Tous droits réservés.
83
Classe de clé primaire
package mypackage1;
import java.io.Serializable;
public class CompteEJBPK implements Serializable {
public int noCompte;
// La classe PK doit
conteneur)
public CompteEJBPK(){}
avoir
un
constructeur
vide
(utilisé
par
le
public CompteEJBPK(int noCompte){
this.noCompte = noCompte;
}
public boolean equals(Object unObjet){
return super.equals(unObjet);
}
public int hashCode(){
return super.hashCode();
}
}
21/04/2017
© Robert Godin. Tous droits réservés.
84
Interface remote
package mypackage1;
import javax.ejb.EJBObject;
import java.rmi.RemoteException;
import java.sql.Date;
public interface CompteEJB extends EJBObject
{
int getNoCompte() throws RemoteException;
double getSolde() throws RemoteException;
void setSolde(double newSolde) throws RemoteException;
Date getDateOuverture() throws RemoteException;
void setDateOuverture(Date newDateouverture) throws RemoteException;
int getNoClient() throws RemoteException;
void setNoClient(int newNoclient) throws RemoteException;
}
21/04/2017
© Robert Godin. Tous droits réservés.
85
Interface home
package mypackage1;
import javax.ejb.EJBHome;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.FinderException;
import java.util.Collection;
public interface CompteEJBHome extends EJBHome
{
CompteEJB
create(int
noCompte,
java.sql.Date
RemoteException, CreateException;
dateOuverture,
int
noClient)
throws
CompteEJB findByPrimaryKey(CompteEJBPK primaryKey) throws RemoteException, FinderException;
Collection findAll() throws RemoteException, FinderException;
Collection findByNoClient(int noClient) throws RemoteException, FinderException;
}
21/04/2017
© Robert Godin. Tous droits réservés.
86
Classe bean
package mypackage1.impl;
import javax.ejb.EntityBean;
import javax.ejb.EntityContext;
import mypackage1.CompteEJBPK;
import java.sql.Date;
public int getNoCompte(){
return noCompte;
}
public class CompteEJBBean implements EntityBean
{
public EntityContext entityContext;
public int noCompte;
public double solde;
public Date dateOuverture;
public int noClient;
public double getSolde(){
return solde;
}
public void setSolde(double newSolde){
solde = newSolde;
}
public CompteEJBPK ejbCreate(int noCompte, Date dateOuverture, int
noClient) {
this.noCompte = noCompte;
this.dateOuverture = dateOuverture;
this.noClient = noClient;
return null;
}
public Date getDateOuverture(){
return dateOuverture;
}
public void setDateOuverture(Date newDateouverture){
dateOuverture = newDateouverture;
}
public void ejbPostCreate(int noCompte, Date dateOuverture, int
noClient){}
public int getNoClient(){
return noClient;
}
public void ejbActivate(){}
public void ejbLoad(){}
public void setNoClient(int newNoclient){
noClient = newNoclient;
}
public void ejbPassivate(){}
public void ejbRemove() {}
}
public void ejbStore(){}
public void setEntityContext(EntityContext ctx){
this.entityContext = ctx;
}
public void unsetEntityContext(){
this.entityContext = null;
}
…
21/04/2017
© Robert Godin. Tous droits réservés.
87
Descripteur de
déploiement XML
<?xml version = '1.0' encoding = 'windows-1252'?>
<!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans
"http://java.sun.com/j2ee/dtds/ejb-jar_1_1.dtd">
<ejb-jar>
<enterprise-beans>
<entity>
<description>Entity Bean ( Container-managed Persistence )</description>
<display-name>CompteEJB</display-name>
<ejb-name>CompteEJB</ejb-name>
<home>mypackage1.CompteEJBHome</home>
<remote>mypackage1.CompteEJB</remote>
<ejb-class>mypackage1.impl.CompteEJBBean</ejb-class>
<persistence-type>Container</persistence-type>
<prim-key-class>mypackage1.CompteEJBPK</prim-key-class>
<reentrant>False</reentrant>
1.1//EN"
<cmp-field>
<field-name>solde</field-name>
</cmp-field>
<cmp-field>
<field-name>noCompte</field-name>
</cmp-field>
<cmp-field>
<field-name>noClient</field-name>
</cmp-field>
<cmp-field>
<field-name>dateOuverture</field-name>
</cmp-field>
</entity>
</enterprise-beans>
</ejb-jar>
21/04/2017
© Robert Godin. Tous droits réservés.
88
Descripteur spécifique
au conteneur Orion
<?xml version = '1.0' encoding = 'windows-1252'?>
<!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="CompteEJB" copy-by-value="false" data-source="jdbc/ora9igodinDS" exclusive-writeaccess="false" table="COMPTE">
<primkey-mapping>
<cmp-field-mapping>
<fields>
<cmp-field-mapping name="noCompte" persistence-name="NOCOMPTE" persistence-type="NUMBER(22)"/>
</fields>
</cmp-field-mapping>
</primkey-mapping>
<cmp-field-mapping name="solde" persistence-name="SOLDE" persistence-type="NUMBER(10,2)"/>
<cmp-field-mapping name="noCompte"/>
<cmp-field-mapping name="noClient"/>
<cmp-field-mapping name="dateOuverture"/>
</entity-deployment>
</enterprise-beans>
<assembly-descriptor>
<default-method-access>
<security-role-mapping name="<default-ejb-caller-role>" impliesAll="true"/>
</default-method-access>
</assembly-descriptor>
</orion-ejb-jar>
21/04/2017
© Robert Godin. Tous droits réservés.
89
Client du EJB
package Samplemypackage1;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.InitialContext;
import mypackage1.*;
import java.util.*;
public class CompteEJBClient {
public static void main(String [] args){
CompteEJBClient compteEJBClient = new CompteEJBClient();
try{
// Recherche de l'objet local home
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.evermind.server.rmi.RMIInitialContextFactory");
env.put(Context.SECURITY_PRINCIPAL, "admin");
env.put(Context.SECURITY_CREDENTIALS, "welcome");
env.put(Context.PROVIDER_URL, "ormi://localhost:23893/current-workspace-app");
Context ctx = new InitialContext(env);
CompteEJBHome compteEJBHome = (CompteEJBHome)ctx.lookup("CompteEJB");
CompteEJB compteEJB;
// Création d'un Compte
Calendar maintenant = Calendar.getInstance(); // Calendrier avec date actuelle
java.sql.Date dateMaintenant =
new java.sql.Date(maintenant.getTime().getTime());
compteEJB = compteEJBHome.create(1000, dateMaintenant, 10);
21/04/2017
© Robert Godin. Tous droits réservés.
90
Client du EJB (suite)
// Recherche de tous les Comptes
Collection uneCollection = compteEJBHome.findAll();
Iterator unIterator = uneCollection.iterator();
System.out.println("Liste des comptes");
System.out.println("noCompte\tsolde\tdateOuverture\tnoClient");
while (unIterator.hasNext()){
compteEJB = (CompteEJB)unIterator.next();
System.out.println(
compteEJB.getNoCompte()+"\t"+
compteEJB.getSolde()+"\t"+
compteEJB.getDateOuverture()+"\t"+
compteEJB.getNoClient());
}
21/04/2017
© Robert Godin. Tous droits réservés.
91
Client du EJB (suite)
// Recherche des Comptes du Client 30
uneCollection = compteEJBHome.findByNoClient(30);
unIterator = uneCollection.iterator();
System.out.println("Liste des comptes du client 30");
System.out.println("noCompte\tsolde\tdateOuverture\tnoClient");
while (unIterator.hasNext()){
compteEJB = (CompteEJB)unIterator.next();
System.out.println(
compteEJB.getNoCompte()+"\t"+
compteEJB.getSolde()+"\t"+
compteEJB.getDateOuverture()+"\t"+
compteEJB.getNoClient());
}
// Recherche du compte 200
compteEJB = compteEJBHome.findByPrimaryKey(new CompteEJBPK(200));
// Extraction du solde
System.out.println("Solde du compte 200 :"+compteEJB.getSolde());
// Modification du solde
compteEJB.setSolde(1000.0);
// Suppression d'un Compte
compteEJB = compteEJBHome.findByPrimaryKey(new CompteEJBPK(400));
compteEJB.remove();
}
catch(Exception lException){
lException.printStackTrace();
}
}
}
21/04/2017
© Robert Godin. Tous droits réservés.
92
Façade EJB session
accède au EJB entité
…
public class BanqueSessionCMDTEJB2Bean implements SessionBean
{
…
public void transferer(int noCompteADebiter, int noCompteACrediter, double montant)
throws EJBException{
// Créer l'objet EJB local (stub RMI)
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.evermind.server.rmi.RMIInitialContextFactory");
env.put(Context.SECURITY_PRINCIPAL, "admin");
env.put(Context.SECURITY_CREDENTIALS, "welcome");
env.put(Context.PROVIDER_URL, "ormi://localhost:23893/current-workspace-app");
try{
Context ctx = new InitialContext(env);
CompteEJBHome compteEJBHome =
(CompteEJBHome)ctx.lookup("CompteEJB");
CompteEJB compteADebiter = compteEJBHome.findByPrimaryKey(new CompteEJBPK(noCompteADebiter));
CompteEJB compteACrediter = compteEJBHome.findByPrimaryKey(new CompteEJBPK(noCompteACrediter));
// Appel de méthode sur l'objet EJB local
compteADebiter.setSolde(compteADebiter.getSolde()-(long)montant);
compteACrediter.setSolde(compteACrediter.getSolde()+(long)montant);
}
catch(Exception lException){
throw new EJBException(lException);
}
}
}
21/04/2017
© Robert Godin. Tous droits réservés.
93
EJB 2

Interfaces locales pour accès léger
–
EJBLocalHome, EJBLocalObject
Gestion des associations par le conteneur
 EJBQL pour la spécification des findXXX()

21/04/2017
© Robert Godin. Tous droits réservés.
94
EJB 3


Simplification de l’architecture EJB
Annotation par métadonnées dans le code
–
–


Mapping objet-relationnel
Comportement transactionnel
EJB codé comme une classe ordinaire (POJO)
Java Persistence API (JPA)
–
–
–
21/04/2017
interface Java indépendante de l’architecture (JSE ou
JEE)
test hors conteneur
pas de home (cycle de vie géré par EntityManager)
© Robert Godin. Tous droits réservés.
95
Exemple avec
JDeveloppeur
Classes EJB entités Editeur et Livre et
annotations générées à partir des tables du
schéma relationnel par le « wizard »
 Association un à plusieurs entre Editeur et
Livre

21/04/2017
© Robert Godin. Tous droits réservés.
96
Classe Editeur
package logiquemetier.persistance;
import …
Annotation @Entity pour EJB entité
@Entity
@Table(name="EDITEUR")
public class Editeur implements Serializable {
@Table pour correspondance
private String nomediteur;
private String ville;
la classe et la table
private Collection<Livre> livreCollection;
public Editeur() { }
entre
public Editeur(String nomediteur, String ville) {
this.nomediteur = nomediteur;
this.ville = ville;
}…
21/04/2017
© Robert Godin. Tous droits réservés.
97
Classe Editeur
@Id pour clé primaire
@Column pour correspondance avec
colonne de la table (sur variable ou
méthodes)
package logiquemetier.persistance;
…
@Entity
@Table(name="EDITEUR")
public class Editeur implements Serializable {
...
@Id
@Column(name="NOMEDITEUR", primaryKey=true, nullable=false)
public String getNomediteur() {
return nomediteur; }
public void setNomediteur(String nomediteur) {
this.nomediteur = nomediteur; }
@Column(name="VILLE", nullable=false)
public String getVille() {
return ville; }
public void setVille(String ville) {
this.ville = ville;} …
21/04/2017
© Robert Godin. Tous droits réservés.
98
Classe Editeur
...
@OneToMany(targetEntity="logiquemetier.persistance.Livre")
@JoinColumn(name="LIVRE.NOMEDITEUR",
referencedColumnName="EDITEUR.NOMEDITEUR")
public Collection<Livre> getLivreCollection() {
return livreCollection; }
public void setLivreCollection(Collection<Livre> livreCollection) {
this.livreCollection = livreCollection;}
public Livre addToLivreCollection(Livre livre) {
getLivreCollection().add( livre );
livre.setEditeur( this );
return livre; }
public Livre removeFromLivreCollection(Livre livre) {
getLivreCollection().remove( livre );
livre.setEditeur( null );
return livre;
}
}
21/04/2017
© Robert Godin. Tous droits réservés.
99
Classe Livre
package logiquemetier.persistance;
import …
@Entity
@Table(name="LIVRE")
public class Livre implements Serializable {
private Long anneeparution;
private String code;
private String isbn;
private String titre;
private Editeur editeur;
public Livre() { }
public Livre(Long anneeparution, String code, String isbn, Editeur editeur, String
titre) {
this.anneeparution = anneeparution;
this.code = code;
this.isbn = isbn;
this.editeur = editeur;
this.titre = titre;
}
21/04/2017
© Robert Godin. Tous droits réservés.
100
Classe Livre
…
@Column(name="ANNEEPARUTION", nullable=false)
public Long getAnneeparution() {
return anneeparution;}
public void setAnneeparution(Long anneeparution) {
this.anneeparution = anneeparution;}
@Column(name="CODE", nullable=false)
public String getCode() {
return code;}
public void setCode(String code) {
this.code = code;}
@Id
@Column(name="ISBN", primaryKey=true, nullable=false)
public String getIsbn() {
return isbn;}
public void setIsbn(String isbn) {
this.isbn = isbn;}
@Column(name="TITRE", nullable=false)
public String getTitre() {
return titre;}
public void setTitre(String titre) {
this.titre = titre;}
21/04/2017
© Robert Godin. Tous droits réservés.
101
Classe Livre
…
@ManyToOne(targetEntity="logiquemetier.persistance.Editeur")
@JoinColumn(name="LIVRE.NOMEDITEUR",
referencedColumnName="EDITEUR.NOMEDITEUR")
public Editeur getEditeur() {
return editeur;
}
public void setEditeur(Editeur editeur) {
this.editeur = editeur;
}
}
21/04/2017
© Robert Godin. Tous droits réservés.
102
EJB session pour « CRUD »
(généré par JDevelopper)

Transactions gérées par le conteneur (mode REQUIRES NEW)
package logiquemetier.facade;
import …
import logiquemetier.persistance.Editeur;
import logiquemetier.persistance.Livre;
@Remote
public interface SessionFacadeEJB3 {
List<Livre> findAllLivre() throws NamingException;
Livre createLivre(Long anneeparution, String code, Editeur editeur,
String isbn, String titre) throws NamingException;
List<Editeur> findAllEditeur() throws NamingException;
Editeur createEditeur(String nomediteur,
String ville) throws NamingException;
void updateEntity(Object entity) throws NamingException;
void deleteEntity(Object entity) throws NamingException;
void refreshEntity(Object entity) throws NamingException;
}
21/04/2017
© Robert Godin. Tous droits réservés.
103
EJB session pour « CRUD »
(généré par JDevelopper)
package logiquemetier.facade;
import …
EntityManager est la classe JPA
pour gérer les entités et transactions
@Stateless(name="SessionFacadeEJB3")
public class SessionFacadeEJB3Bean implements SessionFacadeEJB3 {
private EntityManager _entityManager;
public SessionFacadeEJB3Bean() {
}
public EntityManager getEntityManager() {
return _entityManager;
}
@Resource : injection du EntityManager
par le conteneur
Associé à un persistence unit définis dans
descripteur persistence.xml
(paramètres connexion, …)
@Resource
public void setEntityManager(EntityManager entityManager) {
_entityManager = entityManager;
}
21/04/2017
© Robert Godin. Tous droits réservés.
104
EJB session pour « CRUD »
(généré par JDevelopper)
...
public List<Livre> findAllLivre() throws NamingException {
return getEntityManager().createQuery("select object(o) from Livre
o").getResultList();
}
Langage de requête JPQL
public Livre createLivre(Long anneeparution, String code, Editeur editeur,
String isbn,
String titre) throws NamingException {
final Livre livre = new Livre();
Constructeur normal avec new
livre.setAnneeparution(anneeparution);
livre.setCode(code);
livre.setEditeur(editeur);
Appel à persist() rend l’objet persistant
livre.setIsbn(isbn);
Devient géré par le EntityManager :
livre.setTitre(titre);
contexte de persistance
getEntityManager().persist(livre);
return livre;
}
21/04/2017
…
© Robert Godin. Tous droits réservés.
105
EJB session pour « CRUD »
(généré par JDevelopper)
…
public List<Editeur> findAllEditeur() throws NamingException {
return getEntityManager().createQuery("select object(o) from Editeur o").getResultList();
}
public Editeur createEditeur(String nomediteur,
String ville) throws NamingException {
final Editeur editeur = new Editeur();
editeur.setNomediteur(nomediteur);
editeur.setVille(ville);
getEntityManager().persist(editeur);
return editeur;
}
merge() pour rattacher un objet au
contexte de persistance
remove() pour supprimer
public void updateEntity(Object entity) throws NamingException {
getEntityManager().merge( entity );
}
public void deleteEntity(Object entity) throws NamingException {
final EntityManager em = getEntityManager();
em.remove(em.merge(entity));
}
public void refreshEntity(Object entity) throws NamingException {
getEntityManager().refresh( entity );
}
21/04/2017
© Robert Godin. Tous droits réservés.
}
106
Téléchargement