I. Préambule▲
I-A. Avertissement▲
Les premiers mots d'un bébé quand il essaye de parler sont souvent « arheu ! Arheu ! ». C'est un peu dans cet esprit que j'écris ce tutoriel.
Les technologies étudiées dans ce tutoriel sont les JSF et Hibernate.
Ce document est écrit au fur et à mesure que je prends connaissance de cette technologie. Pour la partie Hibernate que j'étudierai en premier, je m'appuie sur la documentation de référenceHibernate, les forums Hibernate et quelques tutoriels référencés sur le site : http://www.hibernate.org.
Pour la partie Java Server Faces (JSF), je m'appuie évidemment fortement sur le tutoriel J2EE à regarder ensuite pour approfondir vos connaissances avec cette technologie. http://java.sun.com/j2ee/1.4/docs/index.html#tutorials.
I-B. Prérequis▲
I-B-1. Connaissances▲
Connaître les technologies servlet et JSP/sevlet de J2EE (voir document pointé par l'URL de sun ci-dessus). Une connaissance minimale des architectures des serveurs web et conteneur de servlet est nécessaire pour la suite.
I-B-2. Logiciels▲
La norme et l'implémentation de référence pour les Java Server Faces nécessitent d'avoir un conteneur de servlet au niveau de la norme servlet 2.3 ou 2.4. Nous allons prendre la norme servlet 2.4 qui est supportée par Tomcat V5.x (ainsi que JSP V2.0). Pour cela, il faut installer une version de TomcatV5. Nous allons utiliser le bundle Jonas4 Tomcat5 pour le mécanisme de déploiement convivial fourni par Jonas.
Pour télécharger Jonas, aller à l'URL : http://forge.objectweb.org/project/showfiles.php?group_id=5.
L'installation du bundle et le paramétrage ne sont pas décrits dans ce document, se référer à l'excellente documentation fournie avec Jonas.
Il faut aussi télécharger l'implémentation de référence de JSF 1.0. On va éviter de télécharger l'ensemble du J2EE, Sun met à disposition une implémentation « standalone » à l'URL suivante : http://java.sun.com/j2ee/javaserverfaces/download.html et choisir dans la page le téléchargement de l'implémentation de référence : JavaServer Faces v1.1 Reference Implementation.
Pour les besoins de stockage des données, nous utiliserons sur Linux une base Postgres en version 7.4. (il existe sous Windows une version équivalente avec le bundle cygwin).
Pour postgres voir le site : http://www.postgresql.org pour le téléchargement et l'installation de la base.
Pour l'accès à la base de données et la gestion de la persistance, j'utiliserai le logiciel de Mapping O/R et de gestion de la persistance Hibernate V 2.1.3.
I-B-3. Operating System utilisé et organisation des répertoires▲
Tout ce qui est décrit par la suite est fait sur un poste équipé de LINUX V2.4.x et d'une machine virtuelle Java 1.4.2.
Comme Tomcat et Hibernate sont full Java, il n'y aura pas (beaucoup) de difficultés à faire l'extrapolation pour Windows.
Le jdk 1.4.2 est situé sous /usr/local/jdk142.
L'implémentation de référence JSF sera installée à part sous /opt/jsf-1_1 et nous servira de répertoire de développement de notre applicatif pour la partie jsf.
Le bundle Jonas4/Tomcat5 est installé sous le répertoire /opt/JONAS_4_0_0.
II. Architecture logicielle▲
II-A. Schéma de l'architecture logicielle▲
La cible de ce tutoriel, est de réaliser une petite application, que nous bâtirons pas à pas pour arriver à l'architecture décrite dans le schéma ci-dessous :
Toutes les briques du schéma ci-dessus vont être décrites dans la suite du document. Par souci de simplification (et compte tenu de la taille de l'application !), nous faisons jouer le rôle de Business Delegate et de Session Façade, au même objet. Dans le cas d'une application de plus grande envergure, il faut décoller l'aspect Business de la présentation si on veut faciliter (à travers le même Business Delegate) l'accès aux services du composant Session Façade (à gros grains) en utilisant d'autres technologies (Client lourd, Corba, RMI, Web services…).
Dans nos exemples, je n'utiliserai pas la partie EJB V2.x, fournie par le conteneur Jonas. Je crois qu'il faut attendre l'intégration des spécifications EJB V3 en cours d'élaboration à ce jour (mai 2004) qui vont révolutionner complètement la gestion des EJB. Le modèle EJB V3 sera assez proche du schéma ci-dessus.
II-B. Description de l'application▲
Dans un premier temps, nous nous limiterons à une application basique qui gère les opérations de consultation, création, modification, suppression d'utilisateurs. Ces utilisateurs seront stockés dans une table unique, dont la structure suivante :
iduser | nom | prénom | password | datecm | issupprimable |
PK : Numérique | chaîne | chaîne | chaîne | date (timestamp) | Numérique (1/0) |
Au fur et à mesure de l'avancement de ce tutoriel, nous ferons évoluer ce modèle de données pour introduire des notions complémentaires.
III. Construction de l'application▲
III-A. Mécanisme de construction▲
La construction de l'application va se dérouler en partant de la database et en remontant vers la partie cliente.
III-B. Partie database▲
La table dans la base de données essai aura comme nom : utilisateurs.
Le script de création de la table utilisateurs est donné par createTableUtilisateurs.sql ci-dessous :
createTableUtilisateurs.sql
DROP TABLE utilisateurs;
DROP SEQUENCE seq_utilisateurs;
CREATE TABLE utilisateurs (
idUser bigint CONSTRAINT CONS_idUser PRIMARY KEY,
nom varchar
(
40
) NOT NULL,
prenom varchar
(
40
) NOT NULL,
password varchar
(
20
) NOT NULL,
datecm timestamp,
issupprimable int
CHECK (
issupprimable =
0
OR issupprimable =
1
)
);
--
--
Creation de la sequence seq_utilisateur pour idUser de la table utilisateurs
--
Attention ceci n'est pas dans la norme SQL
--
CREATE SEQUENCE seq_utilisateurs;
insert into utilisateurs values (
nextval
(
'seq_utilisateurs'
),'admin'
,'admin'
,'admin'
,CURRENT_TIMESTAMP,0
);
Lancer ce script par :
connexion à la base essai : (en fonction de l'existence de contrôle, possibilité d'avoir à saisir un mot de passe, en principe si le fichier pg_hba.conf comporte une ligne qui considère que les connexions locales sont sures (trust en dernière colonne) on n'a pas à saisir de mot de passe).
psql essai
Lancer la commande :
\i <
repertoire_createTableUtilisateurs.sql>/
createTableUtilisateurs.sql
La table utilisateurs, la séquence seq_utilisateurs, l'index cons_iduser sont créés.
select *
from utilisateurs;
iduser |
nom |
prenom |
password |
datecm |
issupprimable
--------+-------+--------+----------+----------------------------+---------------
1
|
admin |
admin |
admin |
2004
-
04
-
28
21
:34
:26.036093
|
0
ligne)
III-C. Partie Hibernate▲
Ce chapitre va être beaucoup plus long… Courage !
III-C-1. Qu'est-ce que HIBERNATE ?▲
Hibernate est un logiciel, écrit en Java, qui permet de faire le mapping entre Objets Java et Objets stockés en base relationnelle. De plus il en assure la persistance.
Hibernate 2.1.3 possède les fonctionnalités d'annuaire JNDI d'enregistrement et localisation des objets, la gestion des transactions. Ce sont les fonctionnalités que nous examinerons dans ce tutoriel.
Hibernate a été écrit sous la responsabilité de Gavin King qui fait partie de l'équipe JBoss.
Site de référence pour récupérer le produit et la documentation :
http://www.hibernate.org/
Récupérer le Document de Référence Hibernate sur lequel s'appuie ce tutoriel.
III-C-2. Installation du produit Hibernate▲
Après avoir récupéré l'archive, j'ai décompressé cette archive sous le répertoire /opt.
Le décompactage a créé l'arborescence /opt/hibernate-2.1.
Placer le driver JDBC de la base sous le répertoire /opt/hibernate-2.1/lib.
Pour postgres 7.4 il s'agit du fichier jar : pg74.1jdbc3.jar.
Modifier le fichier /opt/hibernate-2.1/src/hibernate.properties pour y intégrer les caractéristiques de la base postgresql :
Extrait :
## PostgreSQL
hibernate.dialect net.sf.hibernate.dialect.PostgreSQLDialect
hibernate.connection.driver_class org.postgresql.Driver
hibernate.connection.url jdbc:postgresql:essai
hibernate.connection.username pastjl
hibernate.connection.password
#hibernate.query.substitutions yes 'Y'
, no 'N'
revenir sous /opt/hibernate-2.1 et lancer
ant eg
Puis refaire le test en modifiant la valeur hibernate.show_sql=true dans le fichier /opt/hibernate-2.1/src/hibernate.properties.
##############################
### Miscellaneous Settings ###
##############################
## print all generated SQL to the console
hibernate.show_sql true
et regarder dans la console les traces générées et connectez-vous à la base pour vérifier la création de tables correspondant au code.
Si OK l'installation est correcte.
III-C-3. Un exemple pour voir !▲
En nous inspirant de l'exemple fourni dans le manuel de référence Hibernate, nous allons l'adapter à notre table utilisateurs créée plus haut.
Le schéma de cet exemple logiciel sera le suivant :
Le schéma ci-dessous permet de fixer la chaîne du dialogue, il ne faut y chercher aucun formalisme particulier.
Le ClientTest va nous permettre d'instancier le POJO, et ensuite de jouer avec le POJO, pour créer, modifier, supprimer des utilisateurs de la table utilisateur.
Mais POJO késako ?
Acronyme pour (selon les Auteurs) :
- Plain Old Java Object ;
- Plain Ordinary Java Object.
donc un simple Objet JavaBean, issu de la J2SE (Standard Edition).
Nous allons donc créer une classe Utilisateur1 que nous mapperons avec la table utilisateurs côté postgreSQL.
III-C-3-a. La classe POJO POJOUtilisateur1▲
Cette classe doit être un javabean, donc exposer comme propriété, les colonnes de la table. Ce ne sera pas toujours le cas, mais commençons par faire simple.
Vous trouverez ci-dessous le code complet de notre classe :
POJOUtilisateur1.java :
package
utilisateur1;
public
class
POJOUtilisateur1 {
private
long
idUser;
private
String nom;
private
String prenom;
private
String password;
private
java.util.Date dateCM;
private
int
isSupprimable;
public
void
setIdUser (
long
_id)
{
idUser=
_id;
}
public
long
getIdUser
(
)
{
return
idUser;
}
public
void
setNom (
String _nom)
{
nom=
_nom;
}
public
String getNom
(
)
{
return
nom;
}
public
void
setPrenom (
String _prenom)
{
prenom=
_prenom;
}
public
String getPrenom
(
)
{
return
prenom;
}
public
void
setPassword (
String _password)
{
password=
_password;
}
public
String getPassword
(
)
{
return
password;
}
public
void
setDateCM (
java.util.Date _date)
{
dateCM=
_date;
}
public
java.util.Date getDateCM
(
)
{
return
dateCM;
}
public
void
setIsSupprimable (
int
_isSup)
{
isSupprimable=
_isSup;
}
public
int
getIsSupprimable
(
)
{
return
isSupprimable;
}
}
La compilation ne pose aucun souci par :
javac utilisateur1/*.java
III-C-3-b. Mettre à jour hibernate.properties▲
Il faut maintenant configurer le fichier hibernate.properties.
Nous allons faire une copie de ce fichier sous l'arborescence /home/pastjl/arheu_arheu/samples/ex1 qui correspond à mon espace de travail pour ce premier exemple.
Il nous faut déclarer un pool de connexion pour permettre à Hibernate de se connecter à la base. Hibernate en propose plusieurs, nous allons prendre le pool build-in propre à Hibernate, même s'il n'est pas performant.
Le pool de connexions choisi est paramétré comme indiqué ci-dessous dans l'extrait du fichier hibernate.properties :
## PostgreSQL
hibernate.dialect net.sf.hibernate.dialect.PostgreSQLDialect
hibernate.connection.driver_class org.postgresql.Driver
hibernate.connection.url jdbc:postgresql:essai
hibernate.connection.username pastjl
hibernate.connection.password
hibernate.connexion.pool_size 10
#hibernate.query.substitutions yes 'Y'
, no 'N'
III-C-3-c. Il faut écrire le fichier XML de mapping 0/R▲
Nous l'appellerons (et il faut impérativement respecter ce nommage pour permettre le chargement par le nom de classe => voir plus bas) POJOUtilisateur1.hbm.xml :
POJOUtilisateur1.hbm.xml
<
?xml version=
"1.0"
?>
<!
DOCTYPE hibernate-
mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd"
>
<
hibernate-
mapping>
<
class
name=
"utilisateur1.POJOUtilisateur1"
table=
"utilisateurs"
>
<
id name=
"idUser"
type=
"long"
unsaved-
value=
"null"
column =
"iduser"
>
<
generator class
=
"sequence"
/>
</
id>
<
property name=
"nom"
type=
"string"
/>
<
property name=
"prenom"
type=
"string"
/>
<
property name=
"password"
type=
"string"
/>
<
property name=
"dateCM"
type=
"timestamp"
column=
"datecm"
/>
<
property name=
"isSupprimable"
type=
"integer"
column=
"issupprimable"
/>
</
class
>
</
hibernate-
mapping>
Noter l'élément generator encapsulé dans l'élément id qui correspond à la primary key. Cet élément permet de générer de manière unique une clé primaire. L'attribut class associé définit le moyen de générer cette PK. Avec Postgres, Oracle, DB2 on peut utiliser des séquences. Voir la documentation de Référence Hibernate pour les autres classes de génération (§ 5.1.4.1. generator)
III-C-3-d. Client pour jouer avec POJOUtilisateur1▲
Il faut maintenant écrite le client, pour jouer avec notre classe POJO.
Pour cela, il faut initialiser un contexte, pour que Hibernate puisse retrouver toutes les informations. Il faut que le fichier hibernate.properties soit dans le classpath lors de l'exécution du programme.
Il faut d'autre part que Hibernate puisse charger le mapping O/R déclaré dans notre fichier POJOUtilisateur1.hbm.xml.
Il y a plusieurs façons de faire :
- dans la classe cliente, positionner ce fichier dans le contexte d'exécution (par le fichier ou la classe) ;
- créer un fichier hibernate.cfg.xml, ou le ou les fichiers de mapping sont décrits.
Dans ce second cas notre fichier , un extrait hibernate.cfg.xml aurait la tête suivante :
<
?xml version=
'1.0'
encoding=
'utf-8'
?>
<!
DOCTYPE hibernate-
configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 2.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-2.0.dtd"
>
<
hibernate-
configuration>
<!--
a SessionFactory instance listed as /
jndi/
name -->
<
session-
factory ....>
....
<!--
mapping files -->
<
mapping resource=
"POJOUtilisateur1.hbm.xml"
/>
...
</
session-
factory>
</
hibernate-
configuration>
Pour notre premier exemple , nous utiliserons la première méthode, en configurant notre contexte dans la classe cliente.
Ci-dessous, le code de la classe cliente utilisée (identifié comme ClientTest Figure 2 plus haut) :
ClientEx1.java
package
utilisateur1;
import
net.sf.hibernate.*;
import
net.sf.hibernate.cfg.*;
public
class
ClientEx1
{
public
static
void
main
(
String [] argv) throws
Exception, MappingException,
HibernateException
{
Transaction tx=
null
;
Session session=
null
;
try
{
Configuration cfg =
new
Configuration
(
)
.addClass
(
utilisateur1.POJOUtilisateur1.class
);
SessionFactory sessionFactory =
cfg.buildSessionFactory
(
);
// ouverture de la session
session =
sessionFactory.openSession
(
);
// Creation d'un user
tx =
session.beginTransaction
(
);
POJOUtilisateur1 util1=
new
POJOUtilisateur1
(
);
// on peuple l'objet util
// mais pas la clé primaire, qui nous sera retournée par session.save();
util1.setNom
(
"Pasturel1"
);
util1.setPrenom
(
"jl1"
);
util1.setPassword
(
"pasturel1"
);
util1.setDateCM
(
new
java.util.Date
(
));
util1.setIsSupprimable
(
1
);
// on recupere la primary key
Long id=
(
Long) session.save
(
util1);
tx.commit
(
);
System.out.println
(
"Id sauvegarde = "
+
id.toString
(
));
}
catch
(
Exception e) {
if
(
tx!=
null
) tx.rollback
(
);
throw
e;
}
finally
{
session.close
(
);
}
}
}
J'ai mis ce client dans le même package que le bean POJOUtilisateur1.
Ce premier client créé un utilisateur dans la table utilisateur, en relançant plusieurs fois on crée des lignes identiques sauf pour la primary key évidemment.
Pour compiler, j'ai utilisé le script suivant qui initialise le classpath avec toutes les bibliothèques .jar livrées avec hibernate (c'est facile avec Unix, ce sera plus dur avec Windows => intégrer les classpath un par un).
Le script de compilation compilEx1.ksh :
compilEx1.ksh
#!/
bin/
bash
#Déclaration des CLASSPATH HIBERNATE
HIBERNATE_HOME=/
opt/
hibernate-
2.1
CLASSPATH=
.:./
utilisateur1 :$HIBERNATE_HOME/
hibernate2.jar
for
i in ` ls $HIBERNATE_HOME/
lib/*.jar`; do
CLASSPATH=$CLASSPATH :$i;
done
echo CLASSPATH=$CLASSPATH
export CLASSPATH
javac -classpath $CLASSPATH utilisateur1/ClientEx1.java
Et le script de lancement lanceEx1.ksh très similaire :
#!/
bin/
bash
#Declaration des CLASSPATH HIBERNATE
HIBERNATE_HOME=/
opt/
hibernate-
2.1
CLASSPATH=
.:./
utilisateur1 :$HIBERNATE_HOME/
hibernate2.jar
for
i in ` ls $HIBERNATE_HOME/
lib/*.jar`; do
CLASSPATH=$CLASSPATH :$i;
done
echo CLASSPATH=$CLASSPATH
export CLASSPATH
java -classpath $CLASSPATH utilisateur1.ClientEx1
Noter que le fichier principal est hibernate2.jar.
D'autre part pour que cela fonctionne, il faut ramener des fichiers de configuration de log4j et de gestion de cache sous l'arborescence /home/pastjl/arheu_arheu/samples/ex1 correspondant au répertoire de lancement. Ces fichiers se trouvent sous /opt/hibernate-2.1/src et ce sont : cache.ccf, oscache.properties,ehcache.xml, log4j.properties, treecache.xml. Hibernate utilise le classpath contextuel pour repérer ces fichiers.
Et après plusieurs lancements, la table utilisateurs a l'allure ci-dessous :
essai=>
select *
from utilisateurs;
iduser |
nom |
prenom |
password |
datecm |
issupprimable
--------+-----------+--------+-----------+----------------------------+---------------
1
|
admin |
admin |
admin |
2004
-
04
-
28
21
:34
:26.036093
|
0
24
|
Pasturel1 |
jl1 |
pasturel1 |
2004
-
05
-
07
21
:53
:54.309
|
1
25
|
Pasturel1 |
jl1 |
pasturel1 |
2004
-
05
-
07
21
:54
:22.961
|
1
26
|
Pasturel1 |
jl1 |
pasturel1 |
2004
-
05
-
07
22
:03
:03.054
|
1
27
|
Pasturel1 |
jl1 |
pasturel1 |
2004
-
05
-
07
22
:12
:16.629
|
1
28
|
Pasturel1 |
jl1 |
pasturel1 |
2004
-
05
-
07
22
:21
:10.92
|
1
(
6
lignes)
III-C-3-e. Analyse du code▲
L'analyse du code montre les opérations suivantes :
- initialiser une configuration avec le ou les fichiers de mapping ;
- créer, à partir de la configuration, une fabrique de sessions (nous verrons plus loin, l'utilisation de ThreadLocal, pour faire des sessions Thread safe) => SessionFactory ;
- créer et ouvrir la session par SessionFactory.open() ;
- démarrer une transaction sur la session par Session.beginTransaction() ;
- peupler les properties du POJOUtilisateur1 => séries de set<property> (sauf la pk, car automatique dans notre cas) ;
- commiter la transaction (ou rollbacker en cas d'échec) Transaction.commit() ;
- fermer la session Session.close().
Nota : pour ce premier exemple, vous n'avez besoin d'installer que Hibernate (TOMCAT et JSF ne sont pas encore nécessaires).
III-C-3-f. Jouons un peu plus avec notre POJOUtilisateur1▲
Nous modifions un peu notre client pour récupérer un utilisateur par son pk, modifier son password , supprimer un utilisateur.
On va modifier le password de l'utilisateur d'iduser 24 et supprimer l'utilisateur de iduser=28.
La classe Client_1Ex1.java :
Client_1Ex1.java
package
utilisateur1;
import
net.sf.hibernate.*;
import
net.sf.hibernate.cfg.*;
public
class
Client_1Ex1
{
public
static
void
main
(
String [] argv) throws
Exception, MappingException,
HibernateException
{
Transaction tx=
null
;
Session session=
null
;
POJOUtilisateur1 util1=
null
;
try
{
Configuration cfg =
new
Configuration
(
)
.addClass
(
utilisateur1.POJOUtilisateur1.class
);
SessionFactory sessionFactory =
cfg.buildSessionFactory
(
);
// ouverture de la session
session =
sessionFactory.openSession
(
);
// Modification d'un utilisateur
tx =
session.beginTransaction
(
);
// Récupérons l utilsateur de iduser = 24
util1=
(
POJOUtilisateur1) session.load
(
POJOUtilisateur1.class
,new
Long
(
24
));
// Modifions son password
util1.setPassword
(
"arheu123"
);
// Sauvons l'enregistrement
session.update
(
util1);
// Suppression de l utilisateur dont iduser passé en argument de la fonction main
long
lg=
Long.parseLong
(
argv[0
]);
util1=
(
POJOUtilisateur1) session.load
(
POJOUtilisateur1.class
,new
Long
(
lg));
session.delete
(
util1);
tx.commit
(
);
}
catch
(
Exception e) {
if
(
tx!=
null
) tx.rollback
(
);
throw
e;
}
finally
{
session.close
(
);
}
}
}
Le script de compilation rendu générique genCompilEx1.ksh :
#!/
bin/
bash
#Déclaration des CLASSPATH HIBERNATE
HIBERNATE_HOME=/
opt/
hibernate-
2.1
CLASSPATH=
.:./
utilisateur1 :$HIBERNATE_HOME/
hibernate2.jar
for
i in ` ls $HIBERNATE_HOME/
lib/*.jar`; do
CLASSPATH=$CLASSPATH :$i;
done
echo CLASSPATH=$CLASSPATH
export CLASSPATH
javac -classpath $CLASSPATH utilisateur1/*.java
Le script de lancement rendu aussi plus générique genLanceEx1.ksh :
#!/
bin/
bash
#Déclaration des CLASSPATH HIBERNATE
HIBERNATE_HOME=/
opt/
hibernate-
2.1
CLASSPATH=
.:./
utilisateur1 :$HIBERNATE_HOME/
hibernate2.jar
for
i in ` ls $HIBERNATE_HOME/
lib/*.jar`; do
CLASSPATH=$CLASSPATH :$i;
done
echo CLASSPATH=$CLASSPATH
export CLASSPATH
java -classpath $CLASSPATH $*
On lance :
genLanceEx1.ksh utilisateur1.Client_1Ex1 28
et on a le résultat ci-dessous :
Avant lancement
essai=>
select *
from utilisateurs;
iduser |
nom |
prenom |
password |
datecm |
issupprimable
--------+-----------+--------+-----------+----------------------------+---------------
1
|
admin |
admin |
admin |
2004
-
04
-
28
21
:34
:26.036093
|
0
24
|
Pasturel1 |
jl1 |
pasturel1 |
2004
-
05
-
07
21
:53
:54.309
|
1
25
|
Pasturel1 |
jl1 |
pasturel1 |
2004
-
05
-
07
21
:54
:22.961
|
1
26
|
Pasturel1 |
jl1 |
pasturel1 |
2004
-
05
-
07
22
:03
:03.054
|
1
27
|
Pasturel1 |
jl1 |
pasturel1 |
2004
-
05
-
07
22
:12
:16.629
|
1
28
|
Pasturel1 |
jl1 |
pasturel1 |
2004
-
05
-
07
22
:21
:10.92
|
1
29
|
Pasturel1 |
jl1 |
pasturel1 |
2004
-
05
-
07
22
:49
:18.387
|
1
30
|
Pasturel1 |
jl1 |
pasturel1 |
2004
-
05
-
07
22
:51
:08.809
|
1
(
8
lignes)
Après lancement
essai=>
select *
from utilisateurs;
iduser |
nom |
prenom |
password |
datecm |
issupprimable
--------+-----------+--------+-----------+----------------------------+---------------
1
|
admin |
admin |
admin |
2004
-
04
-
28
21
:34
:26.036093
|
0
25
|
Pasturel1 |
jl1 |
pasturel1 |
2004
-
05
-
07
21
:54
:22.961
|
1
26
|
Pasturel1 |
jl1 |
pasturel1 |
2004
-
05
-
07
22
:03
:03.054
|
1
27
|
Pasturel1 |
jl1 |
pasturel1 |
2004
-
05
-
07
22
:12
:16.629
|
1
29
|
Pasturel1 |
jl1 |
pasturel1 |
2004
-
05
-
07
22
:49
:18.387
|
1
30
|
Pasturel1 |
jl1 |
pasturel1 |
2004
-
05
-
07
22
:51
:08.809
|
1
24
|
Pasturel1 |
jl1 |
arheu123 |
2004
-
05
-
07
21
:53
:54.309
|
1
(
7
lignes)
III-C-3-g. Récapitulation des opérations (cas manuel/sans ide)▲
Une chose à bien retenir, Hibernate est Class-centric (ou plutôt Java-Centric) plutôt que database-centric. Pour bien comprendre Hibernate, il faut raisonner en Objet et pas en termes de SGBD relationnel.
Les opérations à faire sont :
- configurer le fichier hibernate.properties (une fois suffit, on peut ensuite reprendre le même par la suite) ;
- définir le diagramme de classes (dans notre premier exemple, une seule classe) ;
- coder la classe POJO (éventuellement en y intégrant des tags Xdoclet si vous maitrisez) ;
- générer la table ou les tables => dans notre cas une seule table (nous verrons plus tard , que Hibernate propose trois stratégies de création de tables en cas d'héritage entre classes et des outils permettant de créer la base ainsi qu'un IDE Hibern8IDE) ;
- créer ou générer le/les fichiers de mapping O/R <classe>.hbm.xml ;
- créer le client pour utiliser le POJO.
III-C-3-h. Un peu d'information sur l'aspect non threadsafe de la session▲
Une application en général doit gérer une multiplicité de connexions et de transactions, il faut donc assurer que pour chaque session que les variables utilisées ne soient pas accessibles depuis d'autres sessions. En initialisant des sessions comme des ThreadLocal, les instances des variables sont dédiées au Thread en cours. Hibernate fournit une classe utilitaire pour gérer les sessions en ThreadLocal. La classe est recopiée telle quelle du document de référence de Hibernate :
import
net.sf.hibernate.*;
import
net.sf.hibernate.cfg.*;
public
class
HibernateUtil {
private
static
final
SessionFactory sessionFactory;
static
{
try
{
// Create the SessionFactory
sessionFactory =
new
Configuration
(
).configure
(
).buildSessionFactory
(
);
}
catch
(
HibernateException ex) {
throw
new
RuntimeException
(
"Configuration problem : "
+
ex.getMessage
(
), ex);
}
}
public
static
final
ThreadLocal session =
new
ThreadLocal
(
);
public
static
Session currentSession
(
) throws
HibernateException {
Session s =
(
Session) session.get
(
);
// Open a new Session, if this Thread has none yet
if
(
s ==
null
) {
s =
sessionFactory.openSession
(
);
session.set
(
s);
}
return
s;
}
public
static
void
closeSession
(
) throws
HibernateException {
Session s =
(
Session) session.get
(
);
session.set
(
null
);
if
(
s !=
null
)
s.close
(
);
}
}
et un extrait (de la documentation de référence Hibernate) pour son utilisation :
Session session =
HibernateUtil.currentSession
(
);
Transaction tx=
session.beginTransaction
(
);
Cat princess =
new
Cat
(
);
princess.setName
(
"Princess"
);
princess.setSex
(
'F'
);
princess.setWeight
(
7.4
f);
session.save
(
princess);
tx.commit
(
);
HibernateUtil.closeSession
(
);
Mais à manipuler avec précaution, après avoir bien compris la nécessité ou pas d'utiliser le mécanisme ThreadLocal pour rendre les sessions thread-safe.
III-C-4. Un exemple un peu plus complexe▲
Sous la même architecture que l'exemple précédent, nous allons analyser ce que peut faire Hibernate en termes de mapping :
- dans le cas d'un héritage entre classes ;
- dans le cas d'association one-to-many ;
- dans le cas d'association many-to-many.
On trouvera ci-dessous le diagramme de classe, à partir duquel nous allons construire notre fichier de mapping O/R Hibernate. Ce diagramme de classe a été réalisé à l'aide de l'outil UML PoséidonUML CE. L'intérêt de ce type d'outil est qu'il est capable de générer les modèles au format xmi, qui sont utilisables par la suite à travers l'outil AndroMDA (voir documentation de référence Hibernate).
PoséidonUML permet de générer les setter et getter à la création graphique à la "souris" et permet la génération du code source qui nous sert de squelette pour créer les POJO à faire persister.
On peut noter une association polymorphique entre Personne et Utilisateurs2 qui va nous amener à discuter des stratégies de création des tables dans la database.
III-C-4-a. Rappel sur les Collections en Java et utilisation dans Hibernate▲
Les principaux Objets Java utilisés pour assurer les relations entre Classes dans Hibernate sont décrits dessous. Il conviendra de choisir l'objet adapté au contexte de l'application :
- Collection : racine des interfaces Set, SortedSet, List ;
- Set : une collection qui ne contient aucun élément dupliqué ;
- SortedSet : un set dont l'iterator traversera l'ensemble dans l'ordre croissant naturel de ses éléments ou à partir d'un comparateur fourni à la création du SortedSet ;
- List : une collection ordonnée (également connue sous le nom de séquence). L'utilisateur de cette interface a le contrôle précis où chaque élément est inséré dans la liste. L'utilisateur peut accéder à des éléments par leur index (position dans la liste), et rechercher des éléments dans la liste ;
- Map : un objet qui mappe (relie) des clefs à des valeurs. Une map ne peut pas contenir des clefs doubles ; chaque clef identifie une valeur unique ;
- SortedMap : une map organisée dans l'ordre croissant naturel de la clef, ou à partir d'un comparateur fourni à la création du SortedMap.
Toute classe implémentant une ou plusieurs de ces interfaces est candidate pour décrire les relations multiples entre classes.
Tout tableau peut aussi décrire aussi une relation multiple entre classes.
L'aspect bag (panier : où l'on peut avoir des valeurs dupliquées) peut se traiter à travers des objets implémentant l'interface Collection ou List ou bien un tableau.
IMPORTANT : Le type de la propriété à persister dans la classe doit être du type de l'interface et pas de la classe implémentant l'interface : soit la liste exhaustive avec leur interface dérivée (Set, Map, List) et tableau mais pas par exemple (TreeSet, HashSet, HashMap, ArrayList…)
On écrira par exemple :
Set mesFils =
new
HashSet
(
);
mais pas :
HashSet mesFils =
new
HashSet
(
); // erreur
III-C-4-b. Stratégie de création des tables de type "table per class hierarchy"▲
On positionne la classe et ses sous-classes dans une même table. L'inconvénient, c'est que l'on ne peut pas mettre de clause « not-null » dans les propriétés des sous-classes. Nous allons procéder comme suit :
- écrire à la main le fichier de mapping pour chaque classe ;
- créer un fichier de mapping général qui « inclura » tous les fichiers mapping réalisés ci-dessus ;
- utiliser l'outil SchemaExport, intégré au package Hibernate, qui à partir des classes POJO générées et le fichier de mapping général permet de générer la création des tables dans la database (on peut générer réellement les tables dans la database ou récupérer le script de création sous forme texte)
personne.hbm.xml
<
?xml version=
"1.0"
?>
<!
DOCTYPE hibernate-
mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd"
>
<!--
Strategie table-
per-
class
hierarchy mapping -->
<
hibernate-
mapping package
=
"utilisateur2"
>
<
class
name=
"Personne"
table=
"PERSONNES"
discriminator-
value=
"P"
>
<
id name=
"idPers"
column=
"idPers"
type=
"long"
>
<
generator class
=
"sequence"
/>
</
id>
<
discriminator column=
"sousclasse"
type=
"character"
/>
<
property name=
"nom"
column=
"nom"
type=
"string"
/>
<
property name=
"prenom"
column=
"prenom"
type=
"string"
/>
<
set name=
"listutilisateurs2"
>
<
key column=
"idPers"
/>
<
one-
to-
many class
=
"utilisateur2.Utilisateur2"
/>
</
set>
<
subclass name=
"Employe"
discriminator-
value=
"E"
>
<
property name=
"codeInterne"
column=
"codeInterne"
type=
"string"
/>
</
subclass>
<
subclass name=
"Externe"
discriminator-
value=
"X"
>
<
property name=
"societe"
column=
"societe"
type=
"string"
/>
</
subclass>
</
class
>
</
hibernate-
mapping>
Les objets Personne, Externe et Employe sont stockés dans la même table, comme le montrent les scripts de création générés par SchemaExport (nous verrons plus loin son utilisation) :
create table PERSONNES (
idPers INT8 not null
,
sousclasse CHAR
(
1
) not null
,
nom VARCHAR
(
255
),
prenom VARCHAR
(
255
),
codeInterne VARCHAR
(
255
),
societe VARCHAR
(
255
),
primary key (
idPers)
)
La colonne sous-classe accueille le discriminator qui est P pour Personne, E pour Employe et X pour Externe.
La limitation importante c'est que les colonnes des propriétés des sous-classes ne peuvent pas supporter la contrainte not-null.
III-C-4-c. Stratégie de création des tables de type « table per sub-class hierarchy »▲
Le fichier de mapping est donné ci-dessous pour cette stratégie (e tag clé est joined-subclass) :
<
?xml version=
"1.0"
?>
<!
DOCTYPE hibernate-
mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd"
>
<!--
Strategie table-
per-
class
hierarchy mapping -->
<
hibernate-
mapping package
=
"utilisateur2"
>
<
class
name=
"Personne"
table=
"PERSONNES"
>
<
id name=
"idPers"
column=
"idPers"
type=
"long"
>
<
generator class
=
"sequence"
/>
</
id>
<
property name=
"nom"
column=
"nom"
type=
"string"
/>
<
property name=
"prenom"
column=
"prenom"
type=
"string"
/>
<
set name=
"listutilisateurs2"
>
<
key column=
"idPers"
/>
<
one-
to-
many class
=
"utilisateur2.Utilisateur2"
/>
</
set>
<
joined-
subclass name=
"Employe"
table=
"EMPLOYES"
>
<
key column=
"idPers"
/>
<
property name=
"codeInterne"
column=
"codeInterne"
type=
"string"
/>
</
joined-
subclass>
<
joined-
subclass name=
"Externe"
table=
"EXTERNES"
>
<
key column=
"idPers"
/>
<
property name=
"societe"
column=
"societe"
type=
"string"
/>
</
joined-
subclass>
</
class
>
</
hibernate-
mapping>
Il y a génération de trois tables, la classe mère et ses deux sous-classes à l'aide de SchemaExport :
create table EMPLOYES (
idPers INT8 not null
,
codeInterne VARCHAR
(
255
),
primary key (
idPers)
)
...
create table EXTERNES (
idPers INT8 not null
,
societe VARCHAR
(
255
),
primary key (
idPers)
)
create table PERSONNES (
idPers INT8 not null
,
nom VARCHAR
(
255
),
prenom VARCHAR
(
255
),
primary key (
idPers)
)
...
alter table EMPLOYES add constraint FK75C8D6BCB8D3EEB1 foreign key (
idPers) references PERSONNES
...
alter table EXTERNES add constraint FKC21F544EB8D3EEB1 foreign key (
idPers) references PERSONNES
...
Noter la génération des foreign key entre les tables filles et la table mère dans une relation de type one-to-one.
III-C-4-d. Stratégie de création des tables de type " table per concrete class "▲
Il s'agirait dans notre cas de créer deux tables correspondant aux classes Externe et Employe (en effet une personne peut être considérée soit comme un externe soit comme un employé).
Nous sommes hélas dans le cas d'une relation polymorphique one-to-many non-supportée par Hibernate (voir Document de Référence , Chapitre 8.2 Limitations Table 8.1. Features of inheritance mappings). Il faudrait créer deux relations one-to-many entre Employe et Utilisateur2 et entre Externe et Utilisateur2. Je laisse le soin au lecteur de se livrer à cet exercice. ;-) . Mais cela ne représente aucune difficulté majeure.
Remarque importante :
Quelle que soit la stratégie choisie, elle n'a aucun impact sur le code Java des POJO, (Hibernate est java-centric et pas database-Centric).
Hibernate Query Language (HQL) adresse les objets et pas les tables, ce code HQL est aussi indépendant de la stratégie d'implémentation des tables.
La stratégie influence les requêtes en pur SQL, qu'il est possible de lancer à travers Hibernate. Le bon choix étant bien entendu HQL quand cela est possible.
III-C-4-e. Les POJO utilisés dans notre exemple▲
Pour notre exemple, je choisis la stratégie d'une table pour la hiérarchie de classe. Je n'ai pas trouvé de recommandation dans la documentation pour tel ou tel cas d'utilisation. Il faut vérifier le tableau des limitations par rapport à son usage. Il faut voir cela par rapport aux possibilités de performances de la base. Voir avec votre DBA.
La classe Personne et ses deux classes dérivées Externe et Employe :
Personne.java
package
utilisateur2;
import
java.lang.Long;
import
java.lang.String;
import
java.util.*;
public
class
Personne {
private
Long idPers;
private
String nom;
private
String prenom;
// associations
private
Set utilisateur2s; // of type Utilisateur2
// operations
public
void
setIdPers
(
Long _id) {
idPers=
_id;
}
public
Long getIdPers
(
) {
return
idPers;
}
public
Set getUtilisateur2s
(
)
{
return
utilisateur2s;
}
public
void
setUtilisateur2s
(
Set _util)
{
utilisateur2s=
_util;
}
public
String getNom
(
) {
return
nom;
}
// end getNom
public
void
setNom
(
String _nom) {
nom =
_nom;
}
// end setNom
public
String getPrenom
(
) {
return
prenom;
}
// end getPrenom
public
void
setPrenom
(
String _prenom) {
prenom =
_prenom;
}
// end setPrenom
}
// end Personne
Externe.java
package
utilisateur2;
import
java.lang.String;
import
java.util.*;
public
class
Externe extends
Personne {
private
String societe;
public
String getSociete
(
) {
return
societe;
}
// end getSociete
public
void
setSociete
(
String _societe) {
societe =
_societe;
}
// end setSociete
}
// end Externe
À noter simplement l'héritage de Personne (voir le fichier hbm.xml correspondant plus loin).
Employe.java
package
utilisateur2;
import
java.lang.String;
import
java.util.*;
public
class
Employe extends
Personne {
private
String codeInterne;
public
String getCodeInterne
(
) {
return
codeInterne;
}
// end getCodeInterne
public
void
setCodeInterne
(
String _codeInterne) {
codeInterne =
_codeInterne;
}
// end setCodeInterne
}
// end Employe
La classe Utilisateur2
Utilisateur2.java
package
utilisateur2;
import
java.lang.Long;
import
java.lang.String;
import
java.util.*;
import
java.util.Date;
public
class
Utilisateur2 {
// attributes
private
Long idUser;
private
String login;
private
String password;
private
Date dateCM;
private
Integer isSupprimable;
// associations
private
Personne pers;
public
Set connexions ; // of type Connexion
public
Set roles ; // of type Role
// operations
public
Set getConnexions
(
)
{
return
connexions;
}
public
void
setConnexions
(
Set _cons)
{
connexions=
_cons;
}
public
Set getRoles
(
)
{
return
roles;
}
public
void
setRoles
(
Set _roles)
{
roles=
_roles;
}
public
String getLogin
(
) {
return
login;
}
// end getLogin
public
void
setLogin
(
String _login) {
login =
_login;
}
// end setLogin
public
Long getIdUser
(
) {
return
idUser;
}
public
void
setIdUser
(
Long _id){
idUser=
_id;
}
public
Personne getPers
(
) {
return
pers;
}
public
void
setPers
(
Personne _pers){
pers=
_pers;
}
public
String getPassword
(
) {
return
password;
}
// end getPassword
public
void
setPassword
(
String _password) {
password =
_password;
}
// end setPassword
public
Date getDateCM
(
) {
return
dateCM;
}
// end getDateCM
public
void
setDateCM
(
Date _dateCM) {
dateCM =
_dateCM;
}
// end setDateCM
public
Integer getIsSupprimable
(
) {
return
isSupprimable;
}
// end getIsSupprimable
public
void
setIsSupprimable
(
Integer _isSupprimable) {
isSupprimable =
_isSupprimable;
}
// end setIsSupprimable
}
// end Utilisateur2
Règle : si l'utilisateur est supprimé, on va supprimer les connexions associées ainsi que la ligne de la table d'association avec le rôle. Voir le fichier hbm.xml correspondant plus loin et le rôle particulier de l'attribut cascade dans les fichiers de mapping Hibernate.
Note importante :
les associations (relations) entre les objets persistants doivent se faire à l'aide d'attribut qui sont :
- soit des objets persistants ;
- soit des collections d'objets persistants.
Si on avait voulu décrire la relation Personne -> Utilisateur2 par l'attribut idPers de type Long dans Utilisateur2 (à la place de l'attribut pers de type Personne), nous aurions eu à l'exécution l'erreur suivante dans la console Hibernate :
MappingException : no persister for
: java.lang.Long
La classe Connexion
Connexion.java
package
utilisateur2;
import
java.lang.Long;
import
java.util.*;
import
java.util.Date;
public
class
Connexion {
// attributes
private
Long idCnx;
private
Date dateDeb;
private
Date dateFin;
// association
private
Utilisateur2 util;
// operations
public
void
setIdCnx
(
Long _id) {
idCnx=
_id;
}
public
Long getIdCnx
(
) {
return
idCnx;
}
public
void
setUtil
(
Utilisateur2 _id) {
util=
_id;
}
public
Utilisateur2 getUtil
(
) {
return
util;
}
public
Date getDateFin
(
) {
return
dateFin;
}
// end getDateFin
public
void
setDateFin
(
Date _dateFin) {
dateFin =
_dateFin;
}
// end setDateFin
public
Date getDateDeb
(
) {
return
dateDeb;
}
// end getDateDeb
public
void
setDateDeb
(
Date _dateDeb) {
dateDeb =
_dateDeb;
}
// end setDateDeb
}
// end Connexion
La classe Role
Role.java
package
utilisateur2;
import
java.lang.String;
import
java.util.*;
public
class
Role {
// attributes
private
long
idRole;
private
String nom;
// associations
private
Set utilisateur2s ;
// operations
public
Set getUtilisateur2s
(
)
{
return
utilisateur2s;
}
public
void
setUtilisateur2s
(
Set _util)
{
utilisateur2s=
_util;
}
public
long
getIdRole
(
) {
return
idRole;
}
// end getIdRole
public
void
setIdRole
(
long
_idRole) {
idRole =
_idRole;
}
// end setIdRole
public
void
setNom
(
String _nom) {
nom=
_nom;
}
public
String getNom
(
) {
return
nom;
}
}
// end Role
III-C-4-f. Les fichiers de mapping .hbm.xml correspondants.▲
Le fichier de mapping Personne.hbm.xml :
<
?xml version=
"1.0"
?>
<!
DOCTYPE hibernate-
mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd"
>
<!--
Strategie table-
per-
class
hierarchy mapping -->
<
hibernate-
mapping package
=
"utilisateur2"
>
<
class
name=
"Personne"
table=
"PERSONNES"
discriminator-
value=
"P"
>
<
id name=
"idPers"
column=
"idPers"
type=
"long"
>
<
generator class
=
"sequence"
/>
</
id>
<
discriminator column=
"sousclasse"
type=
"character"
/>
<
property name=
"nom"
column=
"nom"
type=
"string"
/>
<
property name=
"prenom"
column=
"prenom"
type=
"string"
/>
<
set name=
"utilisateur2s"
inverse=
"true"
cascade=
"all-delete-orphan"
>
<
key column=
"pers"
/>
<
one-
to-
many class
=
"utilisateur2.Utilisateur2"
/>
</
set>
<
subclass name=
"Employe"
discriminator-
value=
"E"
>
<
property name=
"codeInterne"
column=
"codeInterne"
type=
"string"
/>
</
subclass>
<
subclass name=
"Externe"
discriminator-
value=
"X"
>
<
property name=
"societe"
column=
"societe"
type=
"string"
/>
</
subclass>
</
class
>
</
hibernate-
mapping>
Noter :
les discriminateurs P, E et X permettent de déterminer la classe Personne et ses sous-classes Externe et Employe.
La clause cascade=« all-delete-orphan » pour détruire les fils Utilisateur2 en cas de destruction de la Personne.
Le fichier de mapping Utilisateur2.hbm.xml :
<
?xml version=
"1.0"
?>
<!
DOCTYPE hibernate-
mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd"
>
<
hibernate-
mapping package
=
"utilisateur2"
>
<
class
name=
"Utilisateur2"
table=
"UTILISATEUR2"
>
<
id name=
"idUser"
column=
"idUser"
type=
"long"
>
<
generator class
=
"sequence"
/>
</
id>
<
property name=
"login"
column=
"login"
type=
"string"
/>
<
property name=
"password"
column=
"password"
type=
"string"
/>
<
property name=
"dateCM"
column=
"datecm"
type=
"timestamp"
/>
<
property name=
"isSupprimable"
column=
"issuprimable"
type=
"int"
/>
<
many-
to-
one name=
"pers"
class
=
"utilisateur2.Personne"
not-
null
=
"true"
/>
<
set name=
"connexions"
inverse=
"true"
cascade=
"all-delete-orphan"
>
<
key column=
"util"
/>
<
one-
to-
many class
=
"utilisateur2.Connexion"
/>
</
set>
<
set name=
"roles"
table=
"UTILISATEUR2_ROLES"
>
<
key column=
"idUser"
/>
<
many-
to-
many class
=
"utilisateur2.Role"
column=
"idRole"
/>
</
set>
</
class
>
</
hibernate-
mapping>
Noter la description de deux associations :
one-to-many avec une règle de cascade, many-to-many avec aussi une règle de cascade.
Le fichier de mapping Connexion.hbm.xml :
<
?xml version=
"1.0"
?>
<!
DOCTYPE hibernate-
mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd"
>
<
hibernate-
mapping package
=
"utilisateur2"
>
<
class
name=
"Connexion"
table=
"CONNEXIONS"
>
<
id name=
"idCnx"
column=
"idCnx"
type=
"long"
>
<
generator class
=
"sequence"
/>
</
id>
<
property name=
"dateDeb"
column=
"dateDeb"
type=
"date"
/>
<
property name=
"dateFin"
column=
"dateFin"
type=
"date"
/>
<
many-
to-
one name=
"util"
class
=
"utilisateur2.Utilisateur2"
column=
"util"
not-
null
=
"true"
/>
</
class
>
</
hibernate-
mapping>
Le fichier de mappind Role.hbm.xml :
<
?xml version=
"1.0"
?>
<!
DOCTYPE hibernate-
mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd"
>
<
hibernate-
mapping package
=
"utilisateur2"
>
<
class
name=
"Role"
table=
"ROLES"
>
<
id name=
"idRole"
column=
"idRole"
type=
"long"
>
<
generator class
=
"sequence"
/>
</
id>
<
property name=
"nom"
column=
"nom"
type=
"string"
/>
<
set name=
"utilisateur2s"
table=
"UTILISATEUR2_ROLES"
inverse=
"true"
>
<
key column=
"idRole"
/>
<
many-
to-
many class
=
"utilisateur2.Utilisateur2"
column=
"idUser"
/>
</
set>
</
class
>
</
hibernate-
mapping>
Pour cet exemple ex2, les fichiers shell, classes et hbm.xml sont localisés sous les répertoires décrits dans le tableau suivant :
Répertoires | samples | ex2 | utilisateurs2 |
Fichiers | *.ksh | *.java *.class *.hbm.xml |
III-C-4-g. Génération du script de création de la base (outil SchemaExport)▲
Hibernate fournit en standard un outil permettant de générer la base. On peut :
- directement créer la base (y compris au lancement de l'application => voir document de référence Hibernate , mais on détruit et on recrée à chaque lancement) ;
- récupérer le script de création de base et le lancer après.
J'ai choisi la deuxième solution.
Pour notre exemple ex2, ci-dessous le script de création de notre base :
#!/
bin/
bash
#Déclaration des CLASSPATH HIBERNATE
HIBERNATE_HOME=/
opt/
hibernate-
2.1
CLASSPATH=
.:./
utilisateur2 :$HIBERNATE_HOME/
hibernate2.jar
for
i in ` ls $HIBERNATE_HOME/
lib/*.jar`; do
CLASSPATH=$CLASSPATH :$i;
done
echo CLASSPATH=$CLASSPATH
export CLASSPATH
java -cp $CLASSPATH net.sf.hibernate.tool.hbm2ddl.SchemaExport --text --format
--output=ex2bis.ddl --delimeter=x $*
Et on lance pour notre exemple avec la commande suivante (sur une seule ligne de commande) :
./
genSchema.ksh utilisateur2/
Connexion.hbm.xml utilisateur2/
Personne.hbm.xml
utilisateur2/
Utilisateur2.hbm.xml utilisateur2/
Role.hbm.xml
Le fichier de création de base est le fichier ex2bis.ddl donné ci-dessous :
alter table UTILISATEUR2_ROLES drop constraint FK9C293F8DB8D66786
alter table UTILISATEUR2_ROLES drop constraint FK9C293F8DB8D4FC31
alter table UTILISATEUR2 drop constraint FK4FCF252F3472F6
alter table CONNEXIONS drop constraint FKEFD16ECA36F002
drop table ROLES
drop table UTILISATEUR2_ROLES
drop table UTILISATEUR2
drop table CONNEXIONS
drop table PERSONNES
drop sequence hibernate_sequence
create table ROLES (
idRole INT8 not null
,
nom VARCHAR
(
255
),
primary key (
idRole)
)
create table UTILISATEUR2_ROLES (
idUser INT8 not null
,
idRole INT8 not null
,
primary key (
idRole, idUser)
)
create table UTILISATEUR2 (
idUser INT8 not null
,
login VARCHAR
(
255
),
password VARCHAR
(
255
),
datecm TIMESTAMP,
issuprimable INT4,
pers INT8 not null
,
primary key (
idUser)
)
create table CONNEXIONS (
idCnx INT8 not null
,
dateDeb DATE,
dateFin DATE,
util INT8 not null
,
primary key (
idCnx)
)
create table PERSONNES (
idPers INT8 not null
,
sousclasse CHAR
(
1
) not null
,
nom VARCHAR
(
255
),
prenom VARCHAR
(
255
),
codeInterne VARCHAR
(
255
),
societe VARCHAR
(
255
),
primary key (
idPers)
)
alter table UTILISATEUR2_ROLES add constraint FK9C293F8DB8D66786 foreign key (
idUser) references UTILISATEUR2
alter table UTILISATEUR2_ROLES add constraint FK9C293F8DB8D4FC31 foreign key (
idRole) references ROLES
alter table UTILISATEUR2 add constraint FK4FCF252F3472F6 foreign key (
pers) references PERSONNES
alter table CONNEXIONS add constraint FKEFD16ECA36F002 foreign key (
util) references UTILISATEUR2
create sequence hibernate_sequence
On notera que les ordres ddl ne se terminent pas par un « ; » (je n'ai pas réussi à le faire générer, je pensais que c'était lié au paramètre delimiter mais je n'y suis pas arrivé). Je pense que la génération sans « ; » correspond à la syntaxe par accès JDBC à la base. Si on veut utiliser ce script pour le lancer à la main depuis une connexion database, il faudra rajouter un ; à la fin de chaque ordre ddl.
III-C-4-h. Création de la database à l'ide du script ddl généré▲
Nous lancerons ce script de manière manuelle. Après rajout des ; ce script a la forme ci-dessous :
ex2bis.ddl
alter table UTILISATEUR2_ROLES drop constraint FK9C293F8DB8D66786;
alter table UTILISATEUR2_ROLES drop constraint FK9C293F8DB8D4FC31;
alter table UTILISATEUR2 drop constraint FK4FCF252F3472F6;
alter table CONNEXIONS drop constraint FKEFD16ECA36F002;
drop table ROLES;
drop table UTILISATEUR2_ROLES;
drop table UTILISATEUR2;
drop table CONNEXIONS;
drop table PERSONNES;
drop sequence hibernate_sequence;
create table ROLES (
idRole INT8 not null
,
nom VARCHAR
(
255
),
primary key (
idRole)
);
create table UTILISATEUR2_ROLES (
idUser INT8 not null
,
idRole INT8 not null
,
primary key (
idRole, idUser)
);
create table UTILISATEUR2 (
idUser INT8 not null
,
login VARCHAR
(
255
),
password VARCHAR
(
255
),
datecm TIMESTAMP,
issuprimable INT4,
pers INT8 not null
,
primary key (
idUser)
);
create table CONNEXIONS (
idCnx INT8 not null
,
dateDeb DATE,
dateFin DATE,
util INT8 not null
,
primary key (
idCnx)
);
create table PERSONNES (
idPers INT8 not null
,
sousclasse CHAR
(
1
) not null
,
nom VARCHAR
(
255
),
prenom VARCHAR
(
255
),
codeInterne VARCHAR
(
255
),
societe VARCHAR
(
255
),
primary key (
idPers)
);
alter table UTILISATEUR2_ROLES add constraint FK9C293F8DB8D66786 foreign key (
idUser) references UTILISATEUR2;
alter table UTILISATEUR2_ROLES add constraint FK9C293F8DB8D4FC31 foreign key (
idRole) references ROLES;
alter table UTILISATEUR2 add constraint FK4FCF252F3472F6 foreign key (
pers) references PERSONNES;
alter table CONNEXIONS add constraint FKEFD16ECA36F002 foreign key (
util) references UTILISATEUR2;
create sequence hibernate_sequence;
Lancer la base posgres, par les commandes postgres de démarrage du postmaster.
Sous votre user unix habituel (et non celui de postgres), se positionner sous l'arborescence ex2 et se connecter à la base essai :
psql essai
et lancer le script :
\i ex2bis.ddl
On peut visualiser les tables créées ainsi que leur structure. Ci-dessous un extrait de cette analyse.
essai=>
\dt
Liste des relations
Schéma |
Nom |
Type |
Propriétaire
--------+--------------------+-------+--------------
public
|
auctionitem |
table |
pastjl
public
|
auctionuser |
table |
pastjl
public
|
bid |
table |
pastjl
public
|
connexions |
table |
pastjl
public
|
personnes |
table |
pastjl
public
|
roles |
table |
pastjl
public
|
utilisateur2 |
table |
pastjl
public
|
utilisateur2_roles |
table |
pastjl
public
|
utilisateurs |
table |
pastjl
(
9
lignes)
essai=>
\d utilisateur2
Table «public
.utilisateur2»
Colonne |
Type |
Modificateurs
--------------+-----------------------------+---------------
iduser |
bigint |
not null
login |
character varying
(
255
) |
password |
character varying
(
255
) |
datecm |
timestamp without time zone |
issuprimable |
integer |
pers |
bigint |
not null
Index :
«utilisateur2_pkey» clé primaire, btree (
iduser)
Contraintes de clés secondaires :
«fk4fcf252f3472f6» FOREIGN KEY (
pers) REFERENCES personnes
(
idpers)
III-C-4-i. Écriture des classes (Facade et Client) d'utilisation des POJO▲
Les fonctions que nous allons implémenter dans cette classe, qui servira ensuite de Session Façade, sont les suivantes :
- création et suppression de personnes ;
- création et suppression de rôles ;
- création et suppression d'utilisateurs (avec un lien sur personne (gestion des orphelins, rôles, connexions) ;
- création et suppression de connexions (gestion des orphelins) ;
- modification Utilisateur (changement mot de passe).
Le schéma de la figure 2 est un peu modifié pour donner le schéma ci-dessous :
Pour la suite , je vais progressivement (même si cela devient verbeux) enrichir :
- la classe java client test : ClientEx2.java ;
- la classe java Session Façade : FaçadeEx2.java ;
- les tables de la databases (conséquences en fait des lancements successifs du client test).
Le bundle des sources des exemples n'intégrera que la version complète des deux classes citées ci-dessus.
III-C-4-j. Premier test basique : création de Personnes à travers les sous-classes Externe et Employe▲
La classe ClientEx2.java :
Création de 8 « Personne » : 4 « Employe » et 4 « Externe »
package
utilisateur2;
import
utilisateur2.*;
import
net.sf.hibernate.HibernateException;
import
net.sf.hibernate.Query;
import
net.sf.hibernate.Session;
import
net.sf.hibernate.SessionFactory;
import
net.sf.hibernate.Transaction;
import
net.sf.hibernate.cfg.Configuration;
public
class
ClientEx2 {
public
static
void
main (
String argv[]) throws
HibernateException {
FacadeEx2 facadeEx2 =
new
FacadeEx2
(
);
try
{
facadeEx2.configure
(
);
}
catch
(
HibernateException he1) {
System.out.println
(
"Error Hibernate "
+
he1.getMessage
(
)+
"
\n
"
);
he1.printStackTrace
(
);
}
// Creation de 4 Employe
facadeEx2.createEmploye
(
"PASTUREL0"
,"JEAN-LOUIS0"
,"PAST0000"
);
facadeEx2.createEmploye
(
"PASTUREL1"
,"JEAN-LOUIS1"
,"PAST0001"
);
facadeEx2.createEmploye
(
"PASTUREL2"
,"JEAN-LOUIS2"
,"PAST0002"
);
facadeEx2.createEmploye
(
"PASTUREL3"
,"JEAN-LOUIS3"
,"PAST0003"
);
// Creation de 4 Externe
facadeEx2.createExterne
(
"EXTERNE0"
,"JEAN-LOUIS0"
,"SOCI0000"
);
facadeEx2.createExterne
(
"EXTERNE1"
,"JEAN-LOUIS1"
,"SOCI0001"
);
facadeEx2.createExterne
(
"EXTERNE2"
,"JEAN-LOUIS2"
,"SOCI0002"
);
facadeEx2.createExterne
(
"EXTERNE3"
,"JEAN-LOUIS3"
,"SOCI0003"
);
}
}
La classe FacadeEx2 .java correspondante :
package
utilisateur2;
import
utilisateur2.*;
import
java.util.*;
import
net.sf.hibernate.HibernateException;
import
net.sf.hibernate.Query;
import
net.sf.hibernate.Session;
import
net.sf.hibernate.SessionFactory;
import
net.sf.hibernate.Transaction;
import
net.sf.hibernate.cfg.Configuration;
public
class
FacadeEx2 {
private
SessionFactory sessFactory;
public
void
configure
(
) throws
HibernateException {
sessFactory =
new
Configuration
(
)
.addClass
(
utilisateur2.Connexion.class
)
.addClass
(
Personne.class
)
.addClass
(
Role.class
)
.addClass
(
Utilisateur2.class
)
.buildSessionFactory
(
);
System.out.println
(
"Configuration termine OK
\n
"
);
}
public
Employe createEmploye
(
String _nom, String _prenom,String _codeInterne)
throws
HibernateException {
//Initialisation d un Employe sous-classe de Personne
Employe emp=
new
Employe
(
);
emp.setNom
(
_nom);
emp.setPrenom
(
_prenom);
emp.setUtilisateur2s
(
new
HashSet
(
));
emp.setCodeInterne
(
_codeInterne);
Session session =
sessFactory.openSession
(
);
Transaction tx =
null
;
try
{
tx =
session.beginTransaction
(
);
session.save
(
emp);
tx.commit
(
);
}
catch
(
HibernateException he) {
if
(
tx!=
null
) tx.rollback
(
);
throw
he;
}
finally
{
session.close
(
);
}
return
emp;
}
public
Externe createExterne
(
String _nom, String _prenom,String _societe)
throws
HibernateException {
//Initialisation d un Employe sous-classe de Personne
Externe ext=
new
Externe
(
);
ext.setNom
(
_nom);
ext.setPrenom
(
_prenom);
ext.setUtilisateur2s
(
new
HashSet
(
));
ext.setSociete
(
_societe);
Session session =
sessFactory.openSession
(
);
Transaction tx =
null
;
try
{
tx =
session.beginTransaction
(
);
session.save
(
ext);
tx.commit
(
);
}
catch
(
HibernateException he) {
if
(
tx!=
null
) tx.rollback
(
);
throw
he;
}
finally
{
session.close
(
);
}
return
ext;
}
}
Après compilation des classes, le lancement s'effectue à l'aide du script lanceEx2.ksh suivant :
#!/
bin/
bash
#Déclaration des CLASSPATH HIBERNATE
HIBERNATE_HOME=/
opt/
hibernate-
2.1
CLASSPATH=
.:./
utilisateur2 :$HIBERNATE_HOME/
hibernate2.jar
for
i in ` ls $HIBERNATE_HOME/
lib/*.jar`; do
CLASSPATH=$CLASSPATH :$i;
done
echo CLASSPATH=$CLASSPATH
export CLASSPATH
java -classpath $CLASSPATH utilisateur2.ClientEx2
./lanceEX2.ksh
et le résultat dans la database :
essai=>
select *
from PERSONNES;
idpers |
sousclasse |
nom |
prenom |
codeinterne |
societe
--------+------------+-----------+-------------+-------------+----------
1
|
E |
PASTUREL0 |
JEAN-
LOUIS0 |
PAST0000 |
2
|
E |
PASTUREL1 |
JEAN-
LOUIS1 |
PAST0001 |
3
|
E |
PASTUREL2 |
JEAN-
LOUIS2 |
PAST0002 |
4
|
E |
PASTUREL3 |
JEAN-
LOUIS3 |
PAST0003 |
5
|
X |
EXTERNE0 |
JEAN-
LOUIS0 |
|
SOCI0000
6
|
X |
EXTERNE1 |
JEAN-
LOUIS1 |
|
SOCI0001
7
|
X |
EXTERNE2 |
JEAN-
LOUIS2 |
|
SOCI0002
8
|
X |
EXTERNE3 |
JEAN-
LOUIS3 |
|
SOCI0003
(
8
lignes)
III-C-4-k. Création de Users▲
On complète les classes ci-dessus. Avant de relancer le client test, on rejouera le script de création de la database qui la détruit et la recrée vide.
Comme nous avons une FK côté Utilisateur2 pointant vers personnes, nous allons affecter tous les utilisateurs à la personne dont le idPers=3, puis nous modifierons certains Utilisateurs pour les affecter à d'autres personnes.
La classe ClientEx2.java :
package
utilisateur2;
import
utilisateur2.*;
import
net.sf.hibernate.HibernateException;
import
net.sf.hibernate.Query;
import
net.sf.hibernate.Session;
import
net.sf.hibernate.SessionFactory;
import
net.sf.hibernate.Transaction;
import
net.sf.hibernate.cfg.Configuration;
public
class
ClientEx2 {
public
static
void
main (
String argv[]) throws
HibernateException {
Employe emp1;
Externe ext1;
Utilisateur2 user1, user2,user3,user4;
FacadeEx2 facadeEx2 =
new
FacadeEx2
(
);
try
{
facadeEx2.configure
(
);
}
catch
(
HibernateException he1) {
System.out.println
(
"Error Hibernate "
+
he1.getMessage
(
)+
"
\n
"
);
he1.printStackTrace
(
);
}
// Creation de 4 Employe
emp1=
facadeEx2.createEmploye
(
"PASTUREL0"
,"JEAN-LOUIS0"
,"PAST0000"
);
facadeEx2.createEmploye
(
"PASTUREL1"
,"JEAN-LOUIS1"
,"PAST0001"
);
facadeEx2.createEmploye
(
"PASTUREL2"
,"JEAN-LOUIS2"
,"PAST0002"
);
facadeEx2.createEmploye
(
"PASTUREL3"
,"JEAN-LOUIS3"
,"PAST0003"
);
// Creation de 4 Externe
ext1=
facadeEx2.createExterne
(
"EXTERNE0"
,"JEAN-LOUIS0"
,"SOCI0000"
);
facadeEx2.createExterne
(
"EXTERNE1"
,"JEAN-LOUIS1"
,"SOCI0001"
);
facadeEx2.createExterne
(
"EXTERNE2"
,"JEAN-LOUIS2"
,"SOCI0002"
);
facadeEx2.createExterne
(
"EXTERNE3"
,"JEAN-LOUIS3"
,"SOCI0003"
);
// Creation de User pour Employe
user1=
facadeEx2.createUtilisateur2
(
"E_USER0_0"
,"E_USER0_0"
,(
int
) 1
);
user2=
facadeEx2.createUtilisateur2
(
"E_USER0_1"
,"E_USER0_1"
,(
int
)1
);
facadeEx2.createUtilisateur2
(
"E_USER1_0"
,"E_USER1_0"
,(
int
)1
);
facadeEx2.createUtilisateur2
(
"E_USER1_1"
,"E_USER1_1"
,(
int
)1
);
facadeEx2.createUtilisateur2
(
"E_USER2_0"
,"E_USER2_0"
,(
int
)1
);
facadeEx2.createUtilisateur2
(
"E_USER2_1"
,"E_USER2_1"
,(
int
)1
);
facadeEx2.createUtilisateur2
(
"E_USER3_0"
,"E_USER3_0"
,(
int
)1
);
facadeEx2.createUtilisateur2
(
"E_USER3_1"
,"E_USER3_1"
,(
int
)1
);
// Creation de User pour Externe
user3=
facadeEx2.createUtilisateur2
(
"X_USER0_0"
,"X_USER0_0"
,(
int
)1
);
user4=
facadeEx2.createUtilisateur2
(
"X_USER0_1"
,"X_USER0_1"
,(
int
)1
);
facadeEx2.createUtilisateur2
(
"X_USER1_0"
,"X_USER1_0"
,(
int
)1
);
facadeEx2.createUtilisateur2
(
"X_USER1_1"
,"X_USER1_1"
,(
int
)1
);
facadeEx2.createUtilisateur2
(
"X_USER2_0"
,"X_USER2_0"
,(
int
)1
);
facadeEx2.createUtilisateur2
(
"X_USER2_1"
,"X_USER2_1"
,(
int
)1
);
facadeEx2.createUtilisateur2
(
"X_USER3_0"
,"X_USER3_0"
,(
int
)1
);
facadeEx2.createUtilisateur2
(
"X_USER3_1"
,"X_USER3_1"
,(
int
)1
);
// Associer le user "E_USER_0_0" a l'employe "PASTUREL0"
facadeEx2.assocPersToUser
(
emp1.getIdPers
(
), user1.getIdUser
(
));
// Associer le user "E_USER_0_1" a l'employe "PASTUREL0"
facadeEx2.assocPersToUser
(
emp1.getIdPers
(
), user2.getIdUser
(
));
//Associer le user "X_USER_0_0" a "EXTERNE0"
facadeEx2.assocPersToUser
(
ext1.getIdPers
(
), user3.getIdUser
(
));
//Associer le user "X_USER_0_1" a "EXTERNE0"
facadeEx2.assocPersToUser
(
ext1.getIdPers
(
), user4.getIdUser
(
));
}
}
La classe FacadeEx2 .java correspondante :
package
utilisateur2;
import
utilisateur2.*;
import
java.util.*;
import
net.sf.hibernate.HibernateException;
import
net.sf.hibernate.Query;
import
net.sf.hibernate.Session;
import
net.sf.hibernate.SessionFactory;
import
net.sf.hibernate.Transaction;
import
net.sf.hibernate.cfg.Configuration;
public
class
FacadeEx2 {
private
SessionFactory sessFactory;
public
void
configure
(
) throws
HibernateException {
sessFactory =
new
Configuration
(
)
.addClass
(
utilisateur2.Connexion.class
)
.addClass
(
Personne.class
)
.addClass
(
Role.class
)
.addClass
(
Utilisateur2.class
)
.buildSessionFactory
(
);
System.out.println
(
"Configuration termine OK
\n
"
);
}
public
Employe createEmploye
(
String _nom, String _prenom,String _codeInterne)
throws
HibernateException {
//Initialisation d un Employe sous-classe de Personne
Employe emp=
new
Employe
(
);
emp.setNom
(
_nom);
emp.setPrenom
(
_prenom);
emp.setUtilisateur2s
(
new
HashSet
(
));
emp.setCodeInterne
(
_codeInterne);
Session session =
sessFactory.openSession
(
);
Transaction tx =
null
;
try
{
tx =
session.beginTransaction
(
);
session.save
(
emp);
tx.commit
(
);
}
catch
(
HibernateException he) {
if
(
tx!=
null
) tx.rollback
(
);
throw
he;
}
finally
{
session.close
(
);
}
return
emp;
}
public
Externe createExterne
(
String _nom, String _prenom,String _societe)
throws
HibernateException {
//Initialisation d un Employe sous-classe de Personne
Externe ext=
new
Externe
(
);
ext.setNom
(
_nom);
ext.setPrenom
(
_prenom);
ext.setUtilisateur2s
(
new
HashSet
(
));
ext.setSociete
(
_societe);
Session session =
sessFactory.openSession
(
);
Transaction tx =
null
;
try
{
tx =
session.beginTransaction
(
);
session.save
(
ext);
tx.commit
(
);
}
catch
(
HibernateException he) {
if
(
tx!=
null
) tx.rollback
(
);
throw
he;
}
finally
{
session.close
(
);
}
return
ext;
}
public
Utilisateur2 createUtilisateur2
(
String _login, String _password,int
_issupprimable)
throws
HibernateException {
//Initialisation d un Employe sous-classe de Personne
Utilisateur2 user=
new
Utilisateur2
(
);
Personne pers=
new
Personne
(
);
user.setLogin
(
_login);
user.setPassword
(
_password);
user.setIsSupprimable
(
new
Integer
(
_issupprimable));
user.setDateCM
(
new
java.util.Date
(
));
//user.setIdPers(new Long(0));
user.setConnexions
(
new
HashSet
(
));
user.setRoles
(
new
HashSet
(
));
Session session =
sessFactory.openSession
(
);
Transaction tx =
null
;
try
{
tx =
session.beginTransaction
(
);
// pour respecter la clause non-null sur la Foreign-key pointant sur personne on
// affecte par defaut la personne 3
Query q2 =
session.createQuery
(
"from Personne where idPers = 3"
);
pers=
(
Personne) q2.list
(
).get
(
0
);
user.setPers
(
pers);
session.save
(
user);
tx.commit
(
);
}
catch
(
HibernateException he) {
if
(
tx!=
null
) tx.rollback
(
);
throw
he;
}
finally
{
session.close
(
);
}
return
user;
}
public
boolean
assocPersToUser
(
Long _idPers, Long _idUser) throws
HibernateException {
boolean
retour=
true
;
Utilisateur2 user =
new
Utilisateur2
(
);
Personne pers =
new
Personne
(
);
Session session =
sessFactory.openSession
(
);
Transaction tx =
null
;
try
{
tx =
session.beginTransaction
(
);
Query q1=
session.createQuery
(
"from Utilisateur2 where idUser = :idUser"
);
Query q2 =
session.createQuery
(
"from Personne where idPers = :idPers"
);
q1.setParameter
(
"idUser"
, _idUser);
q2.setParameter
(
"idPers"
, _idPers);
user=
(
Utilisateur2) q1.list
(
).get
(
0
);
System.out.println
(
"Login = "
+
user.getLogin
(
));
pers=
(
Personne) q2.list
(
).get
(
0
);
System.out.println (
"Nom de la personne ="
+
pers.getNom
(
)+
" ;idPers = "
+
pers.getIdPers
(
));
user.setPers
((
Personne) pers);
//user.setIdPers(emp.getIdPers());
((
Set)((
Personne)pers).getUtilisateur2s
(
)).add
((
Utilisateur2) user);
session.save
(
user);
tx.commit
(
);
}
catch
(
HibernateException he) {
if
(
tx!=
null
) tx.rollback
(
);
retour =
false
;
throw
he;
}
finally
{
session.close
(
);
}
return
retour;
}
}
Après compilation des classes et recréation de la base (relance de ex2bis.ddl connecté à la base), relance par
./
lanceEX2.ksh
Et on a les résultats suivant dans la database :
essai=>
select *
from UTILISATEUR2 order by 1
;
iduser |
login |
password |
datecm |
issuprimable |
pers
--------+-----------+-----------+-------------------------+--------------+------
9
|
E_USER0_0 |
E_USER0_0 |
2004
-
05
-
20
19
:02
:52.46
|
1
|
1
10
|
E_USER0_1 |
E_USER0_1 |
2004
-
05
-
20
19
:02
:52.584
|
1
|
1
11
|
E_USER1_0 |
E_USER1_0 |
2004
-
05
-
20
19
:02
:52.638
|
1
|
3
12
|
E_USER1_1 |
E_USER1_1 |
2004
-
05
-
20
19
:02
:52.653
|
1
|
3
13
|
E_USER2_0 |
E_USER2_0 |
2004
-
05
-
20
19
:02
:52.685
|
1
|
3
14
|
E_USER2_1 |
E_USER2_1 |
2004
-
05
-
20
19
:02
:52.704
|
1
|
3
15
|
E_USER3_0 |
E_USER3_0 |
2004
-
05
-
20
19
:02
:52.727
|
1
|
3
16
|
E_USER3_1 |
E_USER3_1 |
2004
-
05
-
20
19
:02
:52.794
|
1
|
3
17
|
X_USER0_0 |
X_USER0_0 |
2004
-
05
-
20
19
:02
:52.848
|
1
|
5
18
|
X_USER0_1 |
X_USER0_1 |
2004
-
05
-
20
19
:02
:52.881
|
1
|
5
19
|
X_USER1_0 |
X_USER1_0 |
2004
-
05
-
20
19
:02
:52.939
|
1
|
3
20
|
X_USER1_1 |
X_USER1_1 |
2004
-
05
-
20
19
:02
:52.985
|
1
|
3
21
|
X_USER2_0 |
X_USER2_0 |
2004
-
05
-
20
19
:02
:53.053
|
1
|
3
22
|
X_USER2_1 |
X_USER2_1 |
2004
-
05
-
20
19
:02
:53.162
|
1
|
3
23
|
X_USER3_0 |
X_USER3_0 |
2004
-
05
-
20
19
:02
:53.216
|
1
|
3
24
|
X_USER3_1 |
X_USER3_1 |
2004
-
05
-
20
19
:02
:53.295
|
1
|
3
(
16
lignes)
III-C-4-l. Création de rôles et associations avec des utilisateurs▲
Nous allons créer des rôles de type anonyme :ROLE1, ROLE2,ROLE3… et les associer à des utilisateurs.
1re phase création des rôles. Le fichier FacadeEx2.java est donné ci-dessous avec les modifications :
package
utilisateur2;
import
utilisateur2.*;
import
java.util.*;
import
net.sf.hibernate.HibernateException;
import
net.sf.hibernate.Query;
import
net.sf.hibernate.Session;
import
net.sf.hibernate.SessionFactory;
import
net.sf.hibernate.Transaction;
import
net.sf.hibernate.cfg.Configuration;
public
class
FacadeEx2 {
private
SessionFactory sessFactory;
public
void
configure
(
) throws
HibernateException {
sessFactory =
new
Configuration
(
)
.addClass
(
utilisateur2.Connexion.class
)
.addClass
(
Personne.class
)
.addClass
(
Role.class
)
.addClass
(
Utilisateur2.class
)
.buildSessionFactory
(
);
System.out.println
(
"Configuration termine OK
\n
"
);
}
public
Employe createEmploye
(
String _nom, String _prenom,String _codeInterne)
throws
HibernateException {
//Initialisation d un Employe sous-classe de Personne
Employe emp=
new
Employe
(
);
emp.setNom
(
_nom);
emp.setPrenom
(
_prenom);
emp.setUtilisateur2s
(
new
HashSet
(
));
emp.setCodeInterne
(
_codeInterne);
Session session =
sessFactory.openSession
(
);
Transaction tx =
null
;
try
{
tx =
session.beginTransaction
(
);
session.save
(
emp);
tx.commit
(
);
}
catch
(
HibernateException he) {
if
(
tx!=
null
) tx.rollback
(
);
throw
he;
}
finally
{
session.close
(
);
}
return
emp;
}
public
Externe createExterne
(
String _nom, String _prenom,String _societe)
throws
HibernateException {
//Initialisation d un Employe sous-classe de Personne
Externe ext=
new
Externe
(
);
ext.setNom
(
_nom);
ext.setPrenom
(
_prenom);
ext.setUtilisateur2s
(
new
HashSet
(
));
ext.setSociete
(
_societe);
Session session =
sessFactory.openSession
(
);
Transaction tx =
null
;
try
{
tx =
session.beginTransaction
(
);
session.save
(
ext);
tx.commit
(
);
}
catch
(
HibernateException he) {
if
(
tx!=
null
) tx.rollback
(
);
throw
he;
}
finally
{
session.close
(
);
}
return
ext;
}
public
Utilisateur2 createUtilisateur2
(
String _login, String _password,int
_issupprimable)
throws
HibernateException {
//Initialisation d un Employe sous-classe de Personne
Utilisateur2 user=
new
Utilisateur2
(
);
Personne pers=
new
Personne
(
);
user.setLogin
(
_login);
user.setPassword
(
_password);
user.setIsSupprimable
(
new
Integer
(
_issupprimable));
user.setDateCM
(
new
java.util.Date
(
));
//user.setIdPers(new Long(0));
user.setConnexions
(
new
HashSet
(
));
user.setRoles
(
new
HashSet
(
));
Session session =
sessFactory.openSession
(
);
Transaction tx =
null
;
try
{
tx =
session.beginTransaction
(
);
// pour respecter la clause non-null sur la Foreign-key pointant sur personne on
// affecte par defaut la personne 3
Query q2 =
session.createQuery
(
"from Personne where idPers = 3"
);
pers=
(
Personne) q2.list
(
).get
(
0
);
user.setPers
(
pers);
session.save
(
user);
tx.commit
(
);
}
catch
(
HibernateException he) {
if
(
tx!=
null
) tx.rollback
(
);
throw
he;
}
finally
{
session.close
(
);
}
return
user;
}
public
boolean
assocPersToUser
(
Long _idPers, Long _idUser) throws
HibernateException {
boolean
retour=
true
;
Utilisateur2 user =
new
Utilisateur2
(
);
Personne pers =
new
Personne
(
);
Session session =
sessFactory.openSession
(
);
Transaction tx =
null
;
try
{
tx =
session.beginTransaction
(
);
Query q1=
session.createQuery
(
"from Utilisateur2 where idUser = :idUser"
);
Query q2 =
session.createQuery
(
"from Personne where idPers = :idPers"
);
q1.setParameter
(
"idUser"
, _idUser);
q2.setParameter
(
"idPers"
, _idPers);
user=
(
Utilisateur2) q1.list
(
).get
(
0
);
System.out.println
(
"Login = "
+
user.getLogin
(
));
pers=
(
Personne) q2.list
(
).get
(
0
);
System.out.println (
"Nom de la personne ="
+
pers.getNom
(
)+
" ;idPers = "
+
pers.getIdPers
(
));
user.setPers
((
Personne) pers);
//user.setIdPers(emp.getIdPers());
((
Set)((
Personne)pers).getUtilisateur2s
(
)).add
((
Utilisateur2) user);
session.save
(
user);
tx.commit
(
);
}
catch
(
HibernateException he) {
if
(
tx!=
null
) tx.rollback
(
);
retour =
false
;
throw
he;
}
finally
{
session.close
(
);
}
return
retour;
}
public
Role createRole
(
String _role) throws
HibernateException {
Role role=
new
Role
(
);
role.setNom
(
_role);
Session session =
sessFactory.openSession
(
);
Transaction tx =
null
;
try
{
tx =
session.beginTransaction
(
);
session.save
(
role);
tx.commit
(
);
}
catch
(
HibernateException he) {
if
(
tx!=
null
) tx.rollback
(
);
throw
he;
}
finally
{
session.close
(
);
}
return
role;
}
}
Le code de ClientEx2.java correspondant avec les modifications :
package
utilisateur2;
import
utilisateur2.*;
import
net.sf.hibernate.HibernateException;
import
net.sf.hibernate.Query;
import
net.sf.hibernate.Session;
import
net.sf.hibernate.SessionFactory;
import
net.sf.hibernate.Transaction;
import
net.sf.hibernate.cfg.Configuration;
public
class
ClientEx2 {
public
static
void
main (
String argv[]) throws
HibernateException {
Employe emp1;
Externe ext1;
Utilisateur2 user1, user2,user3,user4;
FacadeEx2 facadeEx2 =
new
FacadeEx2
(
);
try
{
facadeEx2.configure
(
);
}
catch
(
HibernateException he1) {
System.out.println
(
"Error Hibernate "
+
he1.getMessage
(
)+
"
\n
"
);
he1.printStackTrace
(
);
}
// Creation de 4 Employe
emp1=
facadeEx2.createEmploye
(
"PASTUREL0"
,"JEAN-LOUIS0"
,"PAST0000"
);
facadeEx2.createEmploye
(
"PASTUREL1"
,"JEAN-LOUIS1"
,"PAST0001"
);
facadeEx2.createEmploye
(
"PASTUREL2"
,"JEAN-LOUIS2"
,"PAST0002"
);
facadeEx2.createEmploye
(
"PASTUREL3"
,"JEAN-LOUIS3"
,"PAST0003"
);
// Creation de 4 Externe
ext1=
facadeEx2.createExterne
(
"EXTERNE0"
,"JEAN-LOUIS0"
,"SOCI0000"
);
facadeEx2.createExterne
(
"EXTERNE1"
,"JEAN-LOUIS1"
,"SOCI0001"
);
facadeEx2.createExterne
(
"EXTERNE2"
,"JEAN-LOUIS2"
,"SOCI0002"
);
facadeEx2.createExterne
(
"EXTERNE3"
,"JEAN-LOUIS3"
,"SOCI0003"
);
// Creation de User pour Employe
user1=
facadeEx2.createUtilisateur2
(
"E_USER0_0"
,"E_USER0_0"
,(
int
) 1
);
user2=
facadeEx2.createUtilisateur2
(
"E_USER0_1"
,"E_USER0_1"
,(
int
)1
);
facadeEx2.createUtilisateur2
(
"E_USER1_0"
,"E_USER1_0"
,(
int
)1
);
facadeEx2.createUtilisateur2
(
"E_USER1_1"
,"E_USER1_1"
,(
int
)1
);
facadeEx2.createUtilisateur2
(
"E_USER2_0"
,"E_USER2_0"
,(
int
)1
);
facadeEx2.createUtilisateur2
(
"E_USER2_1"
,"E_USER2_1"
,(
int
)1
);
facadeEx2.createUtilisateur2
(
"E_USER3_0"
,"E_USER3_0"
,(
int
)1
);
facadeEx2.createUtilisateur2
(
"E_USER3_1"
,"E_USER3_1"
,(
int
)1
);
// Creation de User pour Externe
user3=
facadeEx2.createUtilisateur2
(
"X_USER0_0"
,"X_USER0_0"
,(
int
)1
);
user4=
facadeEx2.createUtilisateur2
(
"X_USER0_1"
,"X_USER0_1"
,(
int
)1
);
facadeEx2.createUtilisateur2
(
"X_USER1_0"
,"X_USER1_0"
,(
int
)1
);
facadeEx2.createUtilisateur2
(
"X_USER1_1"
,"X_USER1_1"
,(
int
)1
);
facadeEx2.createUtilisateur2
(
"X_USER2_0"
,"X_USER2_0"
,(
int
)1
);
facadeEx2.createUtilisateur2
(
"X_USER2_1"
,"X_USER2_1"
,(
int
)1
);
facadeEx2.createUtilisateur2
(
"X_USER3_0"
,"X_USER3_0"
,(
int
)1
);
facadeEx2.createUtilisateur2
(
"X_USER3_1"
,"X_USER3_1"
,(
int
)1
);
// Associer le user "E_USER_0_0" a l'employe "PASTUREL0"
facadeEx2.assocPersToUser
(
emp1.getIdPers
(
), user1.getIdUser
(
));
// Associer le user "E_USER_0_1" a l'employe "PASTUREL0"
facadeEx2.assocPersToUser
(
emp1.getIdPers
(
), user2.getIdUser
(
));
//Associer le user "X_USER_0_0" a "EXTERNE0"
facadeEx2.assocPersToUser
(
ext1.getIdPers
(
), user3.getIdUser
(
));
//Associer le user "X_USER_0_1" a "EXTERNE0"
facadeEx2.assocPersToUser
(
ext1.getIdPers
(
), user4.getIdUser
(
));
// Creation de quelques Roles : ROLE1, ROLE2, ROLE3...
String rolei=
"ROLE"
;
for
(
int
i=
0
;i<
10
;i++
)
{
facadeEx2.createRole
(
rolei+
i);
}
}
}
Après avoir recréé la base à partir du script ex2bis.ddl (voir plus haut), compilé les classes, on peut relancer :
./
lanceEx2.ksh
Le résultat dans la base est donné ci-dessous :
essai=>
select *
from Roles;
idrole |
nom
--------+-------
25
|
ROLE0
26
|
ROLE1
27
|
ROLE2
28
|
ROLE3
29
|
ROLE4
30
|
ROLE5
31
|
ROLE6
32
|
ROLE7
33
|
ROLE8
34
|
ROLE9
(
10
lignes)
essai=>
select *
from UTILISATEUR2_ROLES;
iduser |
idrole
--------+--------
(
0
lignes)
essai=>
select *
from UTILISATEUR2 order by 1
;
iduser |
login |
password |
datecm |
issuprimable |
pers
--------+-----------+-----------+-------------------------+--------------+------
9
|
E_USER0_0 |
E_USER0_0 |
2004
-
05
-
21
16
:58
:18.323
|
1
|
1
10
|
E_USER0_1 |
E_USER0_1 |
2004
-
05
-
21
16
:58
:18.413
|
1
|
1
11
|
E_USER1_0 |
E_USER1_0 |
2004
-
05
-
21
16
:58
:18.486
|
1
|
3
12
|
E_USER1_1 |
E_USER1_1 |
2004
-
05
-
21
16
:58
:18.498
|
1
|
3
13
|
E_USER2_0 |
E_USER2_0 |
2004
-
05
-
21
16
:58
:18.516
|
1
|
3
14
|
E_USER2_1 |
E_USER2_1 |
2004
-
05
-
21
16
:58
:18.534
|
1
|
3
15
|
E_USER3_0 |
E_USER3_0 |
2004
-
05
-
21
16
:58
:18.563
|
1
|
3
16
|
E_USER3_1 |
E_USER3_1 |
2004
-
05
-
21
16
:58
:18.611
|
1
|
3
17
|
X_USER0_0 |
X_USER0_0 |
2004
-
05
-
21
16
:58
:18.654
|
1
|
5
18
|
X_USER0_1 |
X_USER0_1 |
2004
-
05
-
21
16
:58
:18.70
|
1
|
5
19
|
X_USER1_0 |
X_USER1_0 |
2004
-
05
-
21
16
:58
:18.741
|
1
|
3
20
|
X_USER1_1 |
X_USER1_1 |
2004
-
05
-
21
16
:58
:18.792
|
1
|
3
21
|
X_USER2_0 |
X_USER2_0 |
2004
-
05
-
21
16
:58
:18.84
|
1
|
3
22
|
X_USER2_1 |
X_USER2_1 |
2004
-
05
-
21
16
:58
:18.936
|
1
|
3
23
|
X_USER3_0 |
X_USER3_0 |
2004
-
05
-
21
16
:58
:18.986
|
1
|
3
24
|
X_USER3_1 |
X_USER3_1 |
2004
-
05
-
21
16
:58
:19.058
|
1
|
3
(
16
lignes)
2e phase : nous allons maintenant réaliser les associations Utilisateurs <-> rôles
iduser | idrole |
9 | 25 |
9 | 26 |
9 | 27 |
10 | 25 |
10 | 3 |
10 | 34 |
11 | 27 |
11 | 28 |
11 | 29 |
On a bien des relations many-to-many.
On complète la classe FacadeEx2.java par la méthode qui associe les utilisateurs aux rôles (modifications en fond plus foncé)
package
utilisateur2;
import
utilisateur2.*;
import
java.util.*;
import
net.sf.hibernate.HibernateException;
import
net.sf.hibernate.Query;
import
net.sf.hibernate.Session;
import
net.sf.hibernate.SessionFactory;
import
net.sf.hibernate.Transaction;
import
net.sf.hibernate.cfg.Configuration;
public
class
FacadeEx2 {
private
SessionFactory sessFactory;
public
void
configure
(
) throws
HibernateException {
sessFactory =
new
Configuration
(
)
.addClass
(
utilisateur2.Connexion.class
)
.addClass
(
Personne.class
)
.addClass
(
Role.class
)
.addClass
(
Utilisateur2.class
)
.buildSessionFactory
(
);
System.out.println
(
"Configuration termine OK
\n
"
);
}
public
Employe createEmploye
(
String _nom, String _prenom,String _codeInterne)
throws
HibernateException {
//Initialisation d un Employe sous-classe de Personne
Employe emp=
new
Employe
(
);
emp.setNom
(
_nom);
emp.setPrenom
(
_prenom);
emp.setUtilisateur2s
(
new
HashSet
(
));
emp.setCodeInterne
(
_codeInterne);
Session session =
sessFactory.openSession
(
);
Transaction tx =
null
;
try
{
tx =
session.beginTransaction
(
);
session.save
(
emp);
tx.commit
(
);
}
catch
(
HibernateException he) {
if
(
tx!=
null
) tx.rollback
(
);
throw
he;
}
finally
{
session.close
(
);
}
return
emp;
}
public
Externe createExterne
(
String _nom, String _prenom,String _societe)
throws
HibernateException {
//Initialisation d un Employe sous-classe de Personne
Externe ext=
new
Externe
(
);
ext.setNom
(
_nom);
ext.setPrenom
(
_prenom);
ext.setUtilisateur2s
(
new
HashSet
(
));
ext.setSociete
(
_societe);
Session session =
sessFactory.openSession
(
);
Transaction tx =
null
;
try
{
tx =
session.beginTransaction
(
);
session.save
(
ext);
tx.commit
(
);
}
catch
(
HibernateException he) {
if
(
tx!=
null
) tx.rollback
(
);
throw
he;
}
finally
{
session.close
(
);
}
return
ext;
}
public
Utilisateur2 createUtilisateur2
(
String _login, String _password,int
_issupprimable)
throws
HibernateException {
//Initialisation d un Employe sous-classe de Personne
Utilisateur2 user=
new
Utilisateur2
(
);
Personne pers=
new
Personne
(
);
user.setLogin
(
_login);
user.setPassword
(
_password);
user.setIsSupprimable
(
new
Integer
(
_issupprimable));
user.setDateCM
(
new
java.util.Date
(
));
//user.setIdPers(new Long(0));
user.setConnexions
(
new
HashSet
(
));
user.setRoles
(
new
HashSet
(
));
Session session =
sessFactory.openSession
(
);
Transaction tx =
null
;
try
{
tx =
session.beginTransaction
(
);
// pour respecter la clause non-null sur la Foreign-key pointant sur personne on
// affecte par defaut la personne 3
Query q2 =
session.createQuery
(
"from Personne where idPers = 3"
);
pers=
(
Personne) q2.list
(
).get
(
0
);
user.setPers
(
pers);
session.save
(
user);
tx.commit
(
);
}
catch
(
HibernateException he) {
if
(
tx!=
null
) tx.rollback
(
);
throw
he;
}
finally
{
session.close
(
);
}
return
user;
}
public
boolean
assocPersToUser
(
Long _idPers, Long _idUser) throws
HibernateException {
boolean
retour=
true
;
Utilisateur2 user =
new
Utilisateur2
(
);
Personne pers =
new
Personne
(
);
Session session =
sessFactory.openSession
(
);
Transaction tx =
null
;
try
{
tx =
session.beginTransaction
(
);
Query q1=
session.createQuery
(
"from Utilisateur2 where idUser = :idUser"
);
Query q2 =
session.createQuery
(
"from Personne where idPers = :idPers"
);
q1.setParameter
(
"idUser"
, _idUser);
q2.setParameter
(
"idPers"
, _idPers);
user=
(
Utilisateur2) q1.list
(
).get
(
0
);
System.out.println
(
"Login = "
+
user.getLogin
(
));
pers=
(
Personne) q2.list
(
).get
(
0
);
System.out.println (
"Nom de la personne ="
+
pers.getNom
(
)+
" ;idPers = "
+
pers.getIdPers
(
));
user.setPers
((
Personne) pers);
//user.setIdPers(emp.getIdPers());
((
Set)((
Personne)pers).getUtilisateur2s
(
)).add
((
Utilisateur2) user);
session.save
(
user);
tx.commit
(
);
}
catch
(
HibernateException he) {
if
(
tx!=
null
) tx.rollback
(
);
retour =
false
;
throw
he;
}
finally
{
session.close
(
);
}
return
retour;
}
public
Role createRole
(
String _role) throws
HibernateException {
Role role=
new
Role
(
);
role.setNom
(
_role);
Session session =
sessFactory.openSession
(
);
Transaction tx =
null
;
try
{
tx =
session.beginTransaction
(
);
session.save
(
role);
tx.commit
(
);
}
catch
(
HibernateException he) {
if
(
tx!=
null
) tx.rollback
(
);
throw
he;
}
finally
{
session.close
(
);
}
return
role;
}
public
boolean
assocUtilisateur2Roles
(
Long _idUser, Long _idRole)
throws
HibernateException {
boolean
retour=
true
;
Role rol=
new
Role
(
);
Utilisateur2 util=
new
Utilisateur2
(
);
Session session =
sessFactory.openSession
(
);
Transaction tx =
null
;
try
{
tx =
session.beginTransaction
(
);
Query q1=
session.createQuery
(
"from Utilisateur2 where idUser = :idUser"
);
Query q2 =
session.createQuery
(
"from Role where idRole = :idRole"
);
q1.setParameter
(
"idUser"
, _idUser);
q2.setParameter
(
"idRole"
, _idRole);
util=(
Utilisateur2) q1.list
(
).get
(
0
);
System.out.println
(
"Login = "
+
util.getLogin
(
));
rol=(
Role) q2.list
(
).get
(
0
);
System.out.println (
"Nom du Role ="
+
rol.getNom
(
));
((
Set)util.getRoles
(
)).add
((
Role) rol);
((
Set)rol.getUtilisateur2s
(
)).add
((
Utilisateur2) util);
session.save
(
rol);
session.save
(
util);
tx.commit
(
);
}
catch
(
HibernateException he) {
retour=
false
;
if
(
tx!=
null
) tx.rollback
(
);
throw
he;
}
finally
{
session.close
(
);
}
return
retour;
}
}
Écriture de la classe client pour réaliser ces associations ClientEx2.java, dont les modifications sont identifiées en fond plus foncé :
package
utilisateur2;
import
utilisateur2.*;
import
net.sf.hibernate.HibernateException;
import
net.sf.hibernate.Query;
import
net.sf.hibernate.Session;
import
net.sf.hibernate.SessionFactory;
import
net.sf.hibernate.Transaction;
import
net.sf.hibernate.cfg.Configuration;
public
class
ClientEx2 {
public
static
void
main (
String argv[]) throws
HibernateException {
Employe emp1;
Externe ext1;
Utilisateur2 user1, user2,user3,user4;
FacadeEx2 facadeEx2 =
new
FacadeEx2
(
);
try
{
facadeEx2.configure
(
);
}
catch
(
HibernateException he1) {
System.out.println
(
"Error Hibernate "
+
he1.getMessage
(
)+
"
\n
"
);
he1.printStackTrace
(
);
}
// Creation de 4 Employe
emp1=
facadeEx2.createEmploye
(
"PASTUREL0"
,"JEAN-LOUIS0"
,"PAST0000"
);
facadeEx2.createEmploye
(
"PASTUREL1"
,"JEAN-LOUIS1"
,"PAST0001"
);
facadeEx2.createEmploye
(
"PASTUREL2"
,"JEAN-LOUIS2"
,"PAST0002"
);
facadeEx2.createEmploye
(
"PASTUREL3"
,"JEAN-LOUIS3"
,"PAST0003"
);
// Creation de 4 Externe
ext1=
facadeEx2.createExterne
(
"EXTERNE0"
,"JEAN-LOUIS0"
,"SOCI0000"
);
facadeEx2.createExterne
(
"EXTERNE1"
,"JEAN-LOUIS1"
,"SOCI0001"
);
facadeEx2.createExterne
(
"EXTERNE2"
,"JEAN-LOUIS2"
,"SOCI0002"
);
facadeEx2.createExterne
(
"EXTERNE3"
,"JEAN-LOUIS3"
,"SOCI0003"
);
// Creation de User pour Employe
user1=
facadeEx2.createUtilisateur2
(
"E_USER0_0"
,"E_USER0_0"
,(
int
) 1
);
user2=
facadeEx2.createUtilisateur2
(
"E_USER0_1"
,"E_USER0_1"
,(
int
)1
);
facadeEx2.createUtilisateur2
(
"E_USER1_0"
,"E_USER1_0"
,(
int
)1
);
facadeEx2.createUtilisateur2
(
"E_USER1_1"
,"E_USER1_1"
,(
int
)1
);
facadeEx2.createUtilisateur2
(
"E_USER2_0"
,"E_USER2_0"
,(
int
)1
);
facadeEx2.createUtilisateur2
(
"E_USER2_1"
,"E_USER2_1"
,(
int
)1
);
facadeEx2.createUtilisateur2
(
"E_USER3_0"
,"E_USER3_0"
,(
int
)1
);
facadeEx2.createUtilisateur2
(
"E_USER3_1"
,"E_USER3_1"
,(
int
)1
);
// Creation de User pour Externe
user3=
facadeEx2.createUtilisateur2
(
"X_USER0_0"
,"X_USER0_0"
,(
int
)1
);
user4=
facadeEx2.createUtilisateur2
(
"X_USER0_1"
,"X_USER0_1"
,(
int
)1
);
facadeEx2.createUtilisateur2
(
"X_USER1_0"
,"X_USER1_0"
,(
int
)1
);
facadeEx2.createUtilisateur2
(
"X_USER1_1"
,"X_USER1_1"
,(
int
)1
);
facadeEx2.createUtilisateur2
(
"X_USER2_0"
,"X_USER2_0"
,(
int
)1
);
facadeEx2.createUtilisateur2
(
"X_USER2_1"
,"X_USER2_1"
,(
int
)1
);
facadeEx2.createUtilisateur2
(
"X_USER3_0"
,"X_USER3_0"
,(
int
)1
);
facadeEx2.createUtilisateur2
(
"X_USER3_1"
,"X_USER3_1"
,(
int
)1
);
// Associer le user "E_USER_0_0" a l'employe "PASTUREL0"
facadeEx2.assocPersToUser
(
emp1.getIdPers
(
), user1.getIdUser
(
));
// Associer le user "E_USER_0_1" a l'employe "PASTUREL0"
facadeEx2.assocPersToUser
(
emp1.getIdPers
(
), user2.getIdUser
(
));
//Associer le user "X_USER_0_0" a "EXTERNE0"
facadeEx2.assocPersToUser
(
ext1.getIdPers
(
), user3.getIdUser
(
));
//Associer le user "X_USER_0_1" a "EXTERNE0"
facadeEx2.assocPersToUser
(
ext1.getIdPers
(
), user4.getIdUser
(
));
// Creation de quelqes Roles : ROLE1, ROLE2, ROLE3...
String rolei=
"ROLE"
;
for
(
int
i=
0
;i<
10
;i++
)
{
facadeEx2.createRole
(
rolei+
i);
}
// Creation d'associations entre Utilisateurs et Roles :
facadeEx2.assocUtilisateur2Roles
(
new
Long
(
9
), new
Long
(
25
));
facadeEx2.assocUtilisateur2Roles
(
new
Long
(
9
), new
Long
(
26
));
facadeEx2.assocUtilisateur2Roles
(
new
Long
(
9
), new
Long
(
27
));
facadeEx2.assocUtilisateur2Roles
(
new
Long
(
10
), new
Long
(
25
));
facadeEx2.assocUtilisateur2Roles
(
new
Long
(
10
), new
Long
(
33
));
facadeEx2.assocUtilisateur2Roles
(
new
Long
(
10
), new
Long
(
34
));
facadeEx2.assocUtilisateur2Roles
(
new
Long
(
11
), new
Long
(
27
));
facadeEx2.assocUtilisateur2Roles
(
new
Long
(
11
), new
Long
(
28
));
facadeEx2.assocUtilisateur2Roles
(
new
Long
(
11
), new
Long
(
29
));
}
}
Après avoir recréé la base à partir du script ex2bis.ddl (voir plus haut), compilé les classes, on peut relancer :
./
lanceEx2.ksh
Et la preuve dans la base :
essai=>
select *
from UTILISATEUR2_ROLES;
iduser |
idrole
--------+--------
9
|
25
9
|
26
9
|
27
10
|
25
10
|
33
10
|
34
17
|
27
17
|
28
18
|
29
(
9
lignes)
conforme au tableau donné plus haut.
III-C-4-m. Puissance de la clause de cascade▲
Nous allons écrire une méthode de delete d'une personne.
Le comportement attendu à la destruction de la personne est le suivant :
- destruction de l'utilisateur associé à la personne ;
- destruction des connexions ;
- destruction des liens utilisateur2_role sans destruction du rôle.
Les clauses de cascades sont définies dans les fichiers de mapping *.hbm.xml décrits plus haut.
Pour tester la puissance des clauses de cascade, nous allons introduire des connexions sur des utilisateurs (les utilisateurs 9 et 10 liés à la Personne 1 que l'on va détruire, et positionner des connexions sur les utilisateurs 17 et 18 liés à la personne 5 qui ne sera pas détruite).
La classe FacadeEx2.java :
package
utilisateur2;
import
utilisateur2.*;
import
java.util.*;
import
net.sf.hibernate.HibernateException;
import
net.sf.hibernate.Query;
import
net.sf.hibernate.Session;
import
net.sf.hibernate.SessionFactory;
import
net.sf.hibernate.Transaction;
import
net.sf.hibernate.cfg.Configuration;
public
class
FacadeEx2 {
private
SessionFactory sessFactory;
public
void
configure
(
) throws
HibernateException {
sessFactory =
new
Configuration
(
)
.addClass
(
utilisateur2.Connexion.class
)
.addClass
(
Personne.class
)
.addClass
(
Role.class
)
.addClass
(
Utilisateur2.class
)
.buildSessionFactory
(
);
System.out.println
(
"Configuration termine OK
\n
"
);
}
public
Employe createEmploye
(
String _nom, String _prenom,String _codeInterne)
throws
HibernateException {
//Initialisation d un Employe sous-classe de Personne
Employe emp=
new
Employe
(
);
emp.setNom
(
_nom);
emp.setPrenom
(
_prenom);
emp.setUtilisateur2s
(
new
HashSet
(
));
emp.setCodeInterne
(
_codeInterne);
Session session =
sessFactory.openSession
(
);
Transaction tx =
null
;
try
{
tx =
session.beginTransaction
(
);
session.save
(
emp);
tx.commit
(
);
}
catch
(
HibernateException he) {
if
(
tx!=
null
) tx.rollback
(
);
throw
he;
}
finally
{
session.close
(
);
}
return
emp;
}
public
Externe createExterne
(
String _nom, String _prenom,String _societe)
throws
HibernateException {
//Initialisation d un Employe sous-classe de Personne
Externe ext=
new
Externe
(
);
ext.setNom
(
_nom);
ext.setPrenom
(
_prenom);
ext.setUtilisateur2s
(
new
HashSet
(
));
ext.setSociete
(
_societe);
Session session =
sessFactory.openSession
(
);
Transaction tx =
null
;
try
{
tx =
session.beginTransaction
(
);
session.save
(
ext);
tx.commit
(
);
}
catch
(
HibernateException he) {
if
(
tx!=
null
) tx.rollback
(
);
throw
he;
}
finally
{
session.close
(
);
}
return
ext;
}
public
Utilisateur2 createUtilisateur2
(
String _login, String _password,int
_issupprimable)
throws
HibernateException {
//Initialisation d un Employe sous-classe de Personne
Utilisateur2 user=
new
Utilisateur2
(
);
Personne pers=
new
Personne
(
);
user.setLogin
(
_login);
user.setPassword
(
_password);
user.setIsSupprimable
(
new
Integer
(
_issupprimable));
user.setDateCM
(
new
java.util.Date
(
));
//user.setIdPers(new Long(0));
user.setConnexions
(
new
HashSet
(
));
user.setRoles
(
new
HashSet
(
));
Session session =
sessFactory.openSession
(
);
Transaction tx =
null
;
try
{
tx =
session.beginTransaction
(
);
// pour respecter la clause non-null sur la Foreign-key pointant sur personne on
// affecte par defaut la personne 3
Query q2 =
session.createQuery
(
"from Personne where idPers = 3"
);
pers=
(
Personne) q2.list
(
).get
(
0
);
user.setPers
(
pers);
session.save
(
user);
tx.commit
(
);
}
catch
(
HibernateException he) {
if
(
tx!=
null
) tx.rollback
(
);
throw
he;
}
finally
{
session.close
(
);
}
return
user;
}
public
boolean
assocPersToUser
(
Long _idPers, Long _idUser) throws
HibernateException {
boolean
retour=
true
;
Utilisateur2 user =
new
Utilisateur2
(
);
Personne pers =
new
Personne
(
);
Session session =
sessFactory.openSession
(
);
Transaction tx =
null
;
try
{
tx =
session.beginTransaction
(
);
Query q1=
session.createQuery
(
"from Utilisateur2 where idUser = :idUser"
);
Query q2 =
session.createQuery
(
"from Personne where idPers = :idPers"
);
q1.setParameter
(
"idUser"
, _idUser);
q2.setParameter
(
"idPers"
, _idPers);
user=
(
Utilisateur2) q1.list
(
).get
(
0
);
System.out.println
(
"Login = "
+
user.getLogin
(
));
pers=
(
Personne) q2.list
(
).get
(
0
);
System.out.println (
"Nom de la personne ="
+
pers.getNom
(
)+
" ;idPers = "
+
pers.getIdPers
(
));
user.setPers
((
Personne) pers);
//user.setIdPers(emp.getIdPers());
((
Set)((
Personne)pers).getUtilisateur2s
(
)).add
((
Utilisateur2) user);
session.save
(
user);
session.save
(
pers);
session.flush
(
);
tx.commit
(
);
}
catch
(
HibernateException he) {
if
(
tx!=
null
) tx.rollback
(
);
retour =
false
;
throw
he;
}
finally
{
session.close
(
);
}
return
retour;
}
public
Role createRole
(
String _role) throws
HibernateException {
Role role=
new
Role
(
);
role.setNom
(
_role);
Session session =
sessFactory.openSession
(
);
Transaction tx =
null
;
try
{
tx =
session.beginTransaction
(
);
session.save
(
role);
tx.commit
(
);
}
catch
(
HibernateException he) {
if
(
tx!=
null
) tx.rollback
(
);
throw
he;
}
finally
{
session.close
(
);
}
return
role;
}
public
boolean
assocUtilisateur2Roles
(
Long _idUser, Long _idRole)
throws
HibernateException {
boolean
retour=
true
;
Role rol=
new
Role
(
);
Utilisateur2 util=
new
Utilisateur2
(
);
Session session =
sessFactory.openSession
(
);
Transaction tx =
null
;
try
{
tx =
session.beginTransaction
(
);
/*Query q1=session.createQuery("from Utilisateur2 where idUser = :idUser");
Query q2 = session.createQuery("from Role where idRole = :idRole");
q1.setParameter("idUser", _idUser);
q2.setParameter("idRole", _idRole);*/
util=(
Utilisateur2) session.load
(
Utilisateur2.class
,_idUser);
System.out.println
(
"Login = "
+
util.getLogin
(
));
//rol=(Role) q2.list().get(0);
rol=(
Role) session.load
(
Role.class
,_idRole);
System.out.println (
"Nom du Role ="
+
rol.getNom
(
));
((
Set)util.getRoles
(
)).add
((
Role) rol);
((
Set)rol.getUtilisateur2s
(
)).add
((
Utilisateur2) util);
session.save
(
rol);
session.save
(
util);
session.flush
(
);
tx.commit
(
);
}
catch
(
HibernateException he) {
retour=
false
;
if
(
tx!=
null
) tx.rollback
(
);
throw
he;
}
finally
{
session.close
(
);
}
return
retour;
}
public
boolean
deletePersonne
(
Long _idPers) throws
HibernateException {
boolean
retour=
true
;
Personne pers=
new
Personne
(
);
Session session =
sessFactory.openSession
(
);
Transaction tx =
null
;
try
{
tx =
session.beginTransaction
(
);
/*Query q1=session.createQuery("from Personne where idPers = :idPers");
q1.setParameter("idPers", _idPers);
pers=(Personne) q1.list().get(0);*/
pers=(
Personne) session.load
(
Personne.class
,_idPers);
System.out.println
(
"Login = "
+
pers.getNom
(
));
session.delete
(
pers);
session.flush
(
);
tx.commit
(
);
}
catch
(
HibernateException he) {
retour=
false
;
if
(
tx!=
null
) tx.rollback
(
);
throw
he;
}
finally
{
session.close
(
);
}
return
retour;
}
public
Connexion createConnexion
(
Long _idUser) throws
HibernateException
{
Connexion con =
new
Connexion
(
);
con.setDateDeb
(
new
Date
(
));
con.setDateDeb
(
new
Date
(
));
Session session =
sessFactory.openSession
(
);
Transaction tx =
null
;
try
{
tx =
session.beginTransaction
(
);
Utilisateur2 util=(
Utilisateur2) session.load
(
Utilisateur2.class
,_idUser);
con.setUtil
(
util);
((
Set)util.getConnexions
(
)).add
((
Connexion) con);
session.saveOrUpdate
(
util);
session.saveOrUpdate
(
con);
//session.flush();
tx.commit
(
);
}
catch
(
HibernateException he) {
if
(
tx!=
null
) tx.rollback
(
);
throw
he;
}
finally
{
session.close
(
);
}
return
con;
}
}
La classe ClientEx2.java :
package
utilisateur2;
import
utilisateur2.*;
import
net.sf.hibernate.HibernateException;
import
net.sf.hibernate.Query;
import
net.sf.hibernate.Session;
import
net.sf.hibernate.SessionFactory;
import
net.sf.hibernate.Transaction;
import
net.sf.hibernate.cfg.Configuration;
public
class
ClientEx2 {
public
static
void
main (
String argv[]) throws
HibernateException {
Employe emp1;
Externe ext1;
Utilisateur2 user1, user2,user3,user4;
FacadeEx2 facadeEx2 =
new
FacadeEx2
(
);
try
{
facadeEx2.configure
(
);
}
catch
(
HibernateException he1) {
System.out.println
(
"Error Hibernate "
+
he1.getMessage
(
)+
"
\n
"
);
he1.printStackTrace
(
);
}
// Creation de 4 Employe
emp1=
facadeEx2.createEmploye
(
"PASTUREL0"
,"JEAN-LOUIS0"
,"PAST0000"
);
facadeEx2.createEmploye
(
"PASTUREL1"
,"JEAN-LOUIS1"
,"PAST0001"
);
facadeEx2.createEmploye
(
"PASTUREL2"
,"JEAN-LOUIS2"
,"PAST0002"
);
facadeEx2.createEmploye
(
"PASTUREL3"
,"JEAN-LOUIS3"
,"PAST0003"
);
// Creation de 4 Externe
ext1=
facadeEx2.createExterne
(
"EXTERNE0"
,"JEAN-LOUIS0"
,"SOCI0000"
);
facadeEx2.createExterne
(
"EXTERNE1"
,"JEAN-LOUIS1"
,"SOCI0001"
);
facadeEx2.createExterne
(
"EXTERNE2"
,"JEAN-LOUIS2"
,"SOCI0002"
);
facadeEx2.createExterne
(
"EXTERNE3"
,"JEAN-LOUIS3"
,"SOCI0003"
);
// Creation de User pour Employe
user1=
facadeEx2.createUtilisateur2
(
"E_USER0_0"
,"E_USER0_0"
,(
int
) 1
);
user2=
facadeEx2.createUtilisateur2
(
"E_USER0_1"
,"E_USER0_1"
,(
int
)1
);
facadeEx2.createUtilisateur2
(
"E_USER1_0"
,"E_USER1_0"
,(
int
)1
);
facadeEx2.createUtilisateur2
(
"E_USER1_1"
,"E_USER1_1"
,(
int
)1
);
facadeEx2.createUtilisateur2
(
"E_USER2_0"
,"E_USER2_0"
,(
int
)1
);
facadeEx2.createUtilisateur2
(
"E_USER2_1"
,"E_USER2_1"
,(
int
)1
);
facadeEx2.createUtilisateur2
(
"E_USER3_0"
,"E_USER3_0"
,(
int
)1
);
facadeEx2.createUtilisateur2
(
"E_USER3_1"
,"E_USER3_1"
,(
int
)1
);
// Creation de User pour Externe
user3=
facadeEx2.createUtilisateur2
(
"X_USER0_0"
,"X_USER0_0"
,(
int
)1
);
user4=
facadeEx2.createUtilisateur2
(
"X_USER0_1"
,"X_USER0_1"
,(
int
)1
);
facadeEx2.createUtilisateur2
(
"X_USER1_0"
,"X_USER1_0"
,(
int
)1
);
facadeEx2.createUtilisateur2
(
"X_USER1_1"
,"X_USER1_1"
,(
int
)1
);
facadeEx2.createUtilisateur2
(
"X_USER2_0"
,"X_USER2_0"
,(
int
)1
);
facadeEx2.createUtilisateur2
(
"X_USER2_1"
,"X_USER2_1"
,(
int
)1
);
facadeEx2.createUtilisateur2
(
"X_USER3_0"
,"X_USER3_0"
,(
int
)1
);
facadeEx2.createUtilisateur2
(
"X_USER3_1"
,"X_USER3_1"
,(
int
)1
);
// Associer le user "E_USER_0_0" a l'employe "PASTUREL0"
facadeEx2.assocPersToUser
(
emp1.getIdPers
(
), user1.getIdUser
(
));
System.out.println (
"assoc de l emp :"
+
emp1.getIdPers
(
)+
" à l User : "
+
user1.getIdUser
(
));
// Associer le user "E_USER_0_1" a l'employe "PASTUREL0"
facadeEx2.assocPersToUser
(
emp1.getIdPers
(
), user2.getIdUser
(
));
System.out.println (
"assoc de l emp :"
+
emp1.getIdPers
(
)+
" à l User : "
+
user2.getIdUser
(
));
//Associer le user "X_USER_0_0" a "EXTERNE0"
facadeEx2.assocPersToUser
(
ext1.getIdPers
(
), user3.getIdUser
(
));
System.out.println (
"assoc de l emp :"
+
ext1.getIdPers
(
)+
" à l User : "
+
user3.getIdUser
(
));
//Associer le user "X_USER_0_1" a "EXTERNE0"
facadeEx2.assocPersToUser
(
ext1.getIdPers
(
), user4.getIdUser
(
));
System.out.println (
"assoc de l emp :"
+
ext1.getIdPers
(
)+
" à l User : "
+
user4.getIdUser
(
));
// Creation de quelqes Roles : ROLE1, ROLE2, ROLE3...
String rolei=
"ROLE"
;
for
(
int
i=
0
;i<
10
;i++
)
{
facadeEx2.createRole
(
rolei+
i);
}
// Creation d'associations entre Utilisateurs et Roles :
facadeEx2.assocUtilisateur2Roles
(
new
Long
(
9
), new
Long
(
25
));
facadeEx2.assocUtilisateur2Roles
(
new
Long
(
9
), new
Long
(
26
));
facadeEx2.assocUtilisateur2Roles
(
new
Long
(
9
), new
Long
(
27
));
facadeEx2.assocUtilisateur2Roles
(
new
Long
(
10
), new
Long
(
25
));
facadeEx2.assocUtilisateur2Roles
(
new
Long
(
10
), new
Long
(
33
));
facadeEx2.assocUtilisateur2Roles
(
new
Long
(
10
), new
Long
(
34
));
facadeEx2.assocUtilisateur2Roles
(
new
Long
(
17
), new
Long
(
27
));
facadeEx2.assocUtilisateur2Roles
(
new
Long
(
17
), new
Long
(
28
));
facadeEx2.assocUtilisateur2Roles
(
new
Long
(
18
), new
Long
(
29
));
// creation de Connexions sur Utilisateur 9, 10 et 17 et 18
facadeEx2.createConnexion
(
new
Long
(
9
));
facadeEx2.createConnexion
(
new
Long
(
9
));
facadeEx2.createConnexion
(
new
Long
(
10
));
facadeEx2.createConnexion
(
new
Long
(
10
));
facadeEx2.createConnexion
(
new
Long
(
17
));
facadeEx2.createConnexion
(
new
Long
(
17
));
facadeEx2.createConnexion
(
new
Long
(
17
));
facadeEx2.createConnexion
(
new
Long
(
18
));
facadeEx2.createConnexion
(
new
Long
(
18
));
facadeEx2.createConnexion
(
new
Long
(
18
));
facadeEx2.createConnexion
(
new
Long
(
18
));
// Suppression de la Personne dont le idPers = 1
facadeEx2.deletePersonne
(
new
Long
(
1
));
}
}
On va faire deux manipulations :
- la première va consister à ne pas lancer la dernière ligne de ClientEx2 en la diésant pour avoir un état avant ;
- la deuxième intégrera la dernière ligne et nous pourrons faire des comparaisons.
Première manipulation :
Après compilation des classes, rejeu du script de création de la base et exécution du client ClientEx2 , on a la database dans l'état suivant :
essai=>
select *
from Personnes order by 1
;
idpers |
sousclasse |
nom |
prenom |
codeinterne |
societe
--------+------------+-----------+-------------+-------------+----------
1
|
E |
PASTUREL0 |
JEAN-
LOUIS0 |
PAST0000 |
2
|
E |
PASTUREL1 |
JEAN-
LOUIS1 |
PAST0001 |
3
|
E |
PASTUREL2 |
JEAN-
LOUIS2 |
PAST0002 |
4
|
E |
PASTUREL3 |
JEAN-
LOUIS3 |
PAST0003 |
5
|
X |
EXTERNE0 |
JEAN-
LOUIS0 |
|
SOCI0000
6
|
X |
EXTERNE1 |
JEAN-
LOUIS1 |
|
SOCI0001
7
|
X |
EXTERNE2 |
JEAN-
LOUIS2 |
|
SOCI0002
8
|
X |
EXTERNE3 |
JEAN-
LOUIS3 |
|
SOCI0003
(
8
lignes)
select *
from Utilisateur2 order by 1
;
iduser |
login |
password |
datecm |
issuprimable |
pers
--------+-----------+-----------+-------------------------+--------------+------
9
|
E_USER0_0 |
E_USER0_0 |
2004
-
05
-
21
21
:43
:59.31
|
1
|
1
10
|
E_USER0_1 |
E_USER0_1 |
2004
-
05
-
21
21
:43
:59.412
|
1
|
1
11
|
E_USER1_0 |
E_USER1_0 |
2004
-
05
-
21
21
:43
:59.468
|
1
|
3
12
|
E_USER1_1 |
E_USER1_1 |
2004
-
05
-
21
21
:43
:59.485
|
1
|
3
13
|
E_USER2_0 |
E_USER2_0 |
2004
-
05
-
21
21
:43
:59.526
|
1
|
3
14
|
E_USER2_1 |
E_USER2_1 |
2004
-
05
-
21
21
:43
:59.545
|
1
|
3
15
|
E_USER3_0 |
E_USER3_0 |
2004
-
05
-
21
21
:43
:59.567
|
1
|
3
16
|
E_USER3_1 |
E_USER3_1 |
2004
-
05
-
21
21
:43
:59.614
|
1
|
3
17
|
X_USER0_0 |
X_USER0_0 |
2004
-
05
-
21
21
:43
:59.645
|
1
|
5
18
|
X_USER0_1 |
X_USER0_1 |
2004
-
05
-
21
21
:43
:59.691
|
1
|
5
19
|
X_USER1_0 |
X_USER1_0 |
2004
-
05
-
21
21
:43
:59.735
|
1
|
3
20
|
X_USER1_1 |
X_USER1_1 |
2004
-
05
-
21
21
:43
:59.788
|
1
|
3
21
|
X_USER2_0 |
X_USER2_0 |
2004
-
05
-
21
21
:43
:59.843
|
1
|
3
22
|
X_USER2_1 |
X_USER2_1 |
2004
-
05
-
21
21
:43
:59.949
|
1
|
3
23
|
X_USER3_0 |
X_USER3_0 |
2004
-
05
-
21
21
:43
:59.997
|
1
|
3
24
|
X_USER3_1 |
X_USER3_1 |
2004
-
05
-
21
21
:44
:00.064
|
1
|
3
(
16
lignes)
essai=>
select *
from Roles;
idrole |
nom
--------+-------
25
|
ROLE0
26
|
ROLE1
27
|
ROLE2
28
|
ROLE3
29
|
ROLE4
30
|
ROLE5
31
|
ROLE6
32
|
ROLE7
33
|
ROLE8
34
|
ROLE9
(
10
lignes)
essai=>
select *
from Utilisateur2_Roles;
idrole |
iduser
--------+--------
25
|
9
26
|
9
27
|
9
25
|
10
33
|
10
34
|
10
27
|
17
28
|
17
29
|
18
(
9
lignes)
essai=>
select *
from Connexions;
idcnx |
datedeb |
datefin |
util
-------+------------+------------+------
35
|
2004
-
05
-
21
|
2004
-
05
-
21
|
9
36
|
2004
-
05
-
21
|
2004
-
05
-
21
|
9
37
|
2004
-
05
-
21
|
2004
-
05
-
21
|
10
38
|
2004
-
05
-
21
|
2004
-
05
-
21
|
10
39
|
2004
-
05
-
21
|
2004
-
05
-
21
|
17
40
|
2004
-
05
-
21
|
2004
-
05
-
21
|
17
41
|
2004
-
05
-
21
|
2004
-
05
-
21
|
17
42
|
2004
-
05
-
21
|
2004
-
05
-
21
|
18
43
|
2004
-
05
-
21
|
2004
-
05
-
21
|
18
44
|
2004
-
05
-
21
|
2004
-
05
-
21
|
18
45
|
2004
-
05
-
21
|
2004
-
05
-
21
|
18
(
11
lignes)
Nous allons détruire l'Employe 1
. 2e opération, on dé-dièse la dernière commande de ClientEx2, on recompile les classes, on rejoue le script de création de la database et on lance le ClientEx2.
essai=>
select *
from Personnes order by 1
;
idpers |
sousclasse |
nom |
prenom |
codeinterne |
societe
--------+------------+-----------+-------------+-------------+----------
2
|
E |
PASTUREL1 |
JEAN-
LOUIS1 |
PAST0001 |
3
|
E |
PASTUREL2 |
JEAN-
LOUIS2 |
PAST0002 |
4
|
E |
PASTUREL3 |
JEAN-
LOUIS3 |
PAST0003 |
5
|
X |
EXTERNE0 |
JEAN-
LOUIS0 |
|
SOCI0000
6
|
X |
EXTERNE1 |
JEAN-
LOUIS1 |
|
SOCI0001
7
|
X |
EXTERNE2 |
JEAN-
LOUIS2 |
|
SOCI0002
8
|
X |
EXTERNE3 |
JEAN-
LOUIS3 |
|
SOCI0003
(
7
lignes)
Plus de Personne dont le idPers = 1 !
eessai=>
select *
from Utilisateur2 order by 1
;
iduser |
login |
password |
datecm |
issuprimable |
pers
--------+-----------+-----------+-------------------------+--------------+------
11
|
E_USER1_0 |
E_USER1_0 |
2004
-
05
-
21
21
:58
:13.29
|
1
|
3
12
|
E_USER1_1 |
E_USER1_1 |
2004
-
05
-
21
21
:58
:13.303
|
1
|
3
13
|
E_USER2_0 |
E_USER2_0 |
2004
-
05
-
21
21
:58
:13.321
|
1
|
3
14
|
E_USER2_1 |
E_USER2_1 |
2004
-
05
-
21
21
:58
:13.339
|
1
|
3
15
|
E_USER3_0 |
E_USER3_0 |
2004
-
05
-
21
21
:58
:13.366
|
1
|
3
16
|
E_USER3_1 |
E_USER3_1 |
2004
-
05
-
21
21
:58
:13.409
|
1
|
3
17
|
X_USER0_0 |
X_USER0_0 |
2004
-
05
-
21
21
:58
:13.449
|
1
|
5
18
|
X_USER0_1 |
X_USER0_1 |
2004
-
05
-
21
21
:58
:13.50
|
1
|
5
19
|
X_USER1_0 |
X_USER1_0 |
2004
-
05
-
21
21
:58
:13.543
|
1
|
3
20
|
X_USER1_1 |
X_USER1_1 |
2004
-
05
-
21
21
:58
:13.601
|
1
|
3
21
|
X_USER2_0 |
X_USER2_0 |
2004
-
05
-
21
21
:58
:13.663
|
1
|
3
22
|
X_USER2_1 |
X_USER2_1 |
2004
-
05
-
21
21
:58
:13.754
|
1
|
3
23
|
X_USER3_0 |
X_USER3_0 |
2004
-
05
-
21
21
:58
:13.804
|
1
|
3
24
|
X_USER3_1 |
X_USER3_1 |
2004
-
05
-
21
21
:58
:13.868
|
1
|
3
(
14
lignes)
Et là plus d'Utilisateur2 dont l'idUser = 9 ou 10
essai=>
select *
from Utilisateur2_Roles;
idrole |
iduser
--------+--------
27
|
17
28
|
17
29
|
18
(
3
lignes)
Les associations aux rôles des utilisateurs 9 et 10 n'existent plus, les autres n'ont pas été touchées.
essai=>
select *
from Roles;
idrole |
nom
--------+-------
25
|
ROLE0
26
|
ROLE1
27
|
ROLE2
28
|
ROLE3
29
|
ROLE4
30
|
ROLE5
31
|
ROLE6
32
|
ROLE7
33
|
ROLE8
34
|
ROLE9
(
10
lignes)
Les rôles n'ont pas été touchés (comportement attendu et normal).
essai=>
select *
from Connexions;
idcnx |
datedeb |
datefin |
util
-------+------------+------------+------
39
|
2004
-
05
-
21
|
2004
-
05
-
21
|
17
40
|
2004
-
05
-
21
|
2004
-
05
-
21
|
17
41
|
2004
-
05
-
21
|
2004
-
05
-
21
|
17
42
|
2004
-
05
-
21
|
2004
-
05
-
21
|
18
43
|
2004
-
05
-
21
|
2004
-
05
-
21
|
18
44
|
2004
-
05
-
21
|
2004
-
05
-
21
|
18
45
|
2004
-
05
-
21
|
2004
-
05
-
21
|
18
(
7
lignes)
Les connexions des utilisateurs 9 et 10 ont été supprimées, alors que celles des utilisateurs 17 et 18 existent encore (comportement attendu et normal).
Tout ceci est obtenu à l'aide de la clause cascade= « all-delete-orphan » définie dans les fichiers de mapping hbm.xml dans les relations one-to-many.
Extrait de Utilisateur2.hbm.xml :
<
set name=
"connexions"
inverse=
"true"
cascade=
"all-delete-orphan"
>
<
key column=
"util"
/>
<
one-
to-
many class
=
"utilisateur2.Connexion"
/>
</
set>
Extrait de Personne.hbm.xml :
<
set name=
"utilisateur2s"
inverse=
"true"
cascade=
"all-delete-orphan"
>
<
key column=
"pers"
/>
<
one-
to-
many class
=
"utilisateur2.Utilisateur2"
/>
</
set>
Dans les relations many-to-many, c'est la contrainte de foreign key qui traite la suppression des relations orphelines dans la table des associations.
III-C-5. HQL et APICriteria▲
Dans l'exemple précédent, nous avons à peine effleuré le langage HQL (Hibernate Query Langage), nous allons l'utiliser dans un exemple assez simple de requête.
Le but de cet exemple est de trouver la liste des personnes et utilisateurs associés qui se sont déjà connectés (qui ont au moins une ligne dans la table Connexions de notre exemple).
Pour ne pas polluer l'exemple utilisé auparavant, nous allons recréer une Facade et un Client Test spécifique.
III-C-5-a. Traitons d'abord le cas d'utilisation HQL :▲
Le code de la classe FacadeEx2Bis.java est donnée ci-dessous :
package
utilisateur2;
import
utilisateur2.*;
import
java.util.*;
import
net.sf.hibernate.HibernateException;
import
net.sf.hibernate.Query;
import
net.sf.hibernate.Session;
import
net.sf.hibernate.SessionFactory;
import
net.sf.hibernate.Transaction;
import
net.sf.hibernate.cfg.Configuration;
public
class
FacadeEx2Bis {
private
SessionFactory sessFactory;
public
void
configure
(
) throws
HibernateException {
sessFactory =
new
Configuration
(
)
.addClass
(
utilisateur2.Connexion.class
)
.addClass
(
Personne.class
)
.addClass
(
Role.class
)
.addClass
(
Utilisateur2.class
)
.buildSessionFactory
(
);
System.out.println
(
"Configuration termine OK
\n
"
);
}
/* Lister les utilisateurs qui ont ete deja connectes
but : tester HQL
*/
public
TriString[] utilsConnectesHQL
(
) throws
HibernateException {
TriString[] tabTriString ;
ArrayList al=
new
ArrayList
(
);
String str1,str2,str3;
Integer cpt;
Session session =
sessFactory.openSession
(
);
Transaction tx =
null
;
try
{
tx =
session.beginTransaction
(
);
// La requete HQL
Iterator it1 =
session.iterate
(
"select pers.nom,pers.prenom,util.login,count(con) "
+
"from Personne as pers "
+
"join pers.utilisateur2s as util "
+
"join util.connexions as con "
+
" group by pers.nom, pers.prenom,util.login "
+
" having count(con)>0"
);
int
i=
0
;
//
while
(
it1.hasNext
(
)) {
Object[] tuple =
(
Object[]) it1.next
(
);
str1=(
String) tuple[0
];
str2=(
String) tuple[1
];
str3=(
String) tuple[2
];
cpt=(
Integer) tuple[3
];
al.add
(
i,new
TriString
(
str1,str2,str3,cpt));
i++
;
}
// On cree un tableau de la taille de l'it1 pour accueillir les elements
System.out.println (
"Nombre de lignes retournees : "
+
i);
tabTriString=
new
TriString[i];
// on remplit le tableau
for
(
int
j=
0
;j<
i;j++
)
{
tabTriString[j]=(
TriString) al.get
(
j);
}
tx.commit
(
);
}
catch
(
HibernateException he) {
if
(
tx!=
null
) tx.rollback
(
);
throw
he;
}
finally
{
session.close
(
);
}
return
tabTriString;
}
}
Une fois de plus, on notera que la requête adresse les objets et les attributs des objets et non directement les tables. Noter la puissance des join qui évite d'écrire des clauses « where ». On a en effet les liens suivants :
Personnne one-to-many => Utilisateur2 one-to-many => Connexion que retraduisent les « join » de la requête HQL.
La classe ClientEx2Bis.java correspondante :
package
utilisateur2;
import
utilisateur2.*;
import
net.sf.hibernate.HibernateException;
import
net.sf.hibernate.Query;
import
net.sf.hibernate.Session;
import
net.sf.hibernate.SessionFactory;
import
net.sf.hibernate.Transaction;
import
net.sf.hibernate.cfg.Configuration;
public
class
ClientEx2Bis {
public
static
void
main (
String argv[]) throws
HibernateException {
FacadeEx2Bis facadeEx2 =
new
FacadeEx2Bis
(
);
try
{
facadeEx2.configure
(
);
}
catch
(
HibernateException he1) {
System.out.println
(
"Error Hibernate "
+
he1.getMessage
(
)+
"
\n
"
);
he1.printStackTrace
(
);
}
TriString[] triStr=
facadeEx2.utilsConnectesHQL
(
);
// affichage des resultats dans la console :
System.out.println
(
"Personne.nom | Personne.prenom | Utilisateur2.login | nbConnexions"
);
for
(
int
i=
0
;i<
triStr.length;i++
) {
System.out.println
(
triStr[i].string1+
" | "
+
triStr[i].string2+
" | "
+
triStr[i].string3+
" | "
+
triStr[i].cpt.toString
(
));
}
}
}
La classe utilitaire TriString.java utilisée :
package
utilisateur2;
public
class
TriString {
public
String string1=
""
;
public
String string2=
""
;
public
String string3=
""
;
public
Integer cpt;
public
TriString
(
String str1,String str2,String str3,Integer _cpt)
{
string1=
str1;
string2=
str2;
string3=
str3;
cpt=
_cpt;
}
}
Comme d'habitude on compile l'ensemble des classes, mais on ne relance pas la création de la database, on l'utilise telle que l'avait laissée le dernier exemple.
Les tables sont dans l'état suivant :
essai=>
select *
from Personnes order by 1
;
idpers |
sousclasse |
nom |
prenom |
codeinterne |
societe
--------+------------+-----------+-------------+-------------+----------
2
|
E |
PASTUREL1 |
JEAN-
LOUIS1 |
PAST0001 |
3
|
E |
PASTUREL2 |
JEAN-
LOUIS2 |
PAST0002 |
4
|
E |
PASTUREL3 |
JEAN-
LOUIS3 |
PAST0003 |
5
|
X |
EXTERNE0 |
JEAN-
LOUIS0 |
|
SOCI0000
6
|
X |
EXTERNE1 |
JEAN-
LOUIS1 |
|
SOCI0001
7
|
X |
EXTERNE2 |
JEAN-
LOUIS2 |
|
SOCI0002
8
|
X |
EXTERNE3 |
JEAN-
LOUIS3 |
|
SOCI0003
(
7
lignes)
essai=>
select *
from Utilisateur2 order by 1
;
iduser |
login |
password |
datecm |
issuprimable |
pers
--------+-----------+-----------+-------------------------+--------------+------
11
|
E_USER1_0 |
E_USER1_0 |
2004
-
05
-
21
21
:58
:13.29
|
1
|
3
12
|
E_USER1_1 |
E_USER1_1 |
2004
-
05
-
21
21
:58
:13.303
|
1
|
3
13
|
E_USER2_0 |
E_USER2_0 |
2004
-
05
-
21
21
:58
:13.321
|
1
|
3
14
|
E_USER2_1 |
E_USER2_1 |
2004
-
05
-
21
21
:58
:13.339
|
1
|
3
15
|
E_USER3_0 |
E_USER3_0 |
2004
-
05
-
21
21
:58
:13.366
|
1
|
3
16
|
E_USER3_1 |
E_USER3_1 |
2004
-
05
-
21
21
:58
:13.409
|
1
|
3
17
|
X_USER0_0 |
X_USER0_0 |
2004
-
05
-
21
21
:58
:13.449
|
1
|
5
18
|
X_USER0_1 |
X_USER0_1 |
2004
-
05
-
21
21
:58
:13.50
|
1
|
5
19
|
X_USER1_0 |
X_USER1_0 |
2004
-
05
-
21
21
:58
:13.543
|
1
|
3
20
|
X_USER1_1 |
X_USER1_1 |
2004
-
05
-
21
21
:58
:13.601
|
1
|
3
21
|
X_USER2_0 |
X_USER2_0 |
2004
-
05
-
21
21
:58
:13.663
|
1
|
3
22
|
X_USER2_1 |
X_USER2_1 |
2004
-
05
-
21
21
:58
:13.754
|
1
|
3
23
|
X_USER3_0 |
X_USER3_0 |
2004
-
05
-
21
21
:58
:13.804
|
1
|
3
24
|
X_USER3_1 |
X_USER3_1 |
2004
-
05
-
21
21
:58
:13.868
|
1
|
3
(
14
lignes)
essai=>
select *
from connexions order by 1
;
idcnx |
datedeb |
datefin |
util
-------+------------+------------+------
39
|
2004
-
05
-
21
|
2004
-
05
-
21
|
17
40
|
2004
-
05
-
21
|
2004
-
05
-
21
|
17
41
|
2004
-
05
-
21
|
2004
-
05
-
21
|
17
42
|
2004
-
05
-
21
|
2004
-
05
-
21
|
18
43
|
2004
-
05
-
21
|
2004
-
05
-
21
|
18
44
|
2004
-
05
-
21
|
2004
-
05
-
21
|
18
45
|
2004
-
05
-
21
|
2004
-
05
-
21
|
18
(
7
lignes)
On voit donc que la Personne 5 (EXTERNE0 , JEAN-LOUIS0) qui possède les Utilisateur2 17 et 18 (resp login X_USER0_0 et X_USER0_1) est la seule à posséder des connexions (4 pour le login X_USER0_1 et 3 pour le login X_USER0_0).
Le lanceur du client ClientEx2Bis.java est donné ci-dessous :
Fichier lanceEx2Bis.ksh :
#!/
bin/
bash
#Déclaration des CLASSPATH HIBERNATE
HIBERNATE_HOME=/
opt/
hibernate-
2.1
CLASSPATH=
.:./
utilisateur2 :$HIBERNATE_HOME/
hibernate2.jar
for
i in ` ls $HIBERNATE_HOME/
lib/*.jar`; do
CLASSPATH=$CLASSPATH :$i;
done
echo CLASSPATH=$CLASSPATH
export CLASSPATH
java -classpath $CLASSPATH utilisateur2.ClientEx2Bis
Le lancement se fait par :
./
lanceEx2Bis.ksh
Dans la console Hibernate on peut noter la requête SQL générée (dans le fichier hibernate.properties, on a mis la clause : hibernate.show_sql true) :
Hibernate : select personne0_.nom as x0_0_, personne0_.prenom as x1_0_, utilisateu1_.login as x2_0_,
count
(
connexions2_.idCnx) as x3_0_
from PERSONNES personne0_
inner join UTILISATEUR2 utilisateu1_ on personne0_.idPers=
utilisateu1_.pers
inner join CONNEXIONS connexions2_ on utilisateu1_.idUser=
connexions2_.util
group by personne0_.nom , personne0_.prenom , utilisateu1_.login
having (
count
(
connexions2_.idCnx)>
0
)
Si on exécute cette requête SQL dans la base on a le résultat suivant :
essai=>
select personne0_.nom as x0_0_, personne0_.prenom as x1_0_, utilisateu1_.login as x2_0_,
count
(
connexions2_.idCnx) as x3_0_
from PERSONNES personne0_
inner join UTILISATEUR2 utilisateu1_ on personne0_.idPers=
utilisateu1_.pers
inner join CONNEXIONS connexions2_ on utilisateu1_.idUser=
connexions2_.util
group by personne0_.nom , personne0_.prenom , utilisateu1_.login
having (
count
(
connexions2_.idCnx)>
0
);
x0_0_ |
x1_0_ |
x2_0_ |
x3_0_
----------+-------------+-----------+-------
EXTERNE0 |
JEAN-
LOUIS0 |
X_USER0_1 |
4
EXTERNE0 |
JEAN-
LOUIS0 |
X_USER0_0 |
3
(
2
lignes)
Ce qui est conforme au résultat attendu du lancement de la classe ClientEx2Bis récupéré dans la console :
Personne.nom |
Personne.prenom |
Utilisateur2.login |
nbConnexions
EXTERNE0 |
JEAN-
LOUIS0 |
X_USER0_1 |
4
EXTERNE0 |
JEAN-
LOUIS0 |
X_USER0_0 |
3
III-C-5-b. Traitement avec l'API Criteria▲
Pour ceux qui ne sont pas habitués à manipuler le langage SQL (orienté relationnel), Hibernate propose comme alternative une API (Criteria) pour exécuter des requêtes.
L'API Criteria ne propose en natif que des requêtes ne portant que sur une classe persistante (Session.createCriteria(Class PersistentClass), ce qui malheureusement ne nous convient pas très bien dans notre cas.
Nous allons exécuter une requête qui va retourner le nom des personnes (et autant de fois que de connexions) ayant eu une connexion. Il faudrait compléter la requête par une requête sur la classe persistante Utilisateur2. Je laisse le soin au lecteur de compléter cette requête.
La classe FacadeEx2Bis.java modifiée :
package
utilisateur2;
import
utilisateur2.*;
import
java.util.*;
import
net.sf.hibernate.expression.Expression;
import
net.sf.hibernate.HibernateException;
import
net.sf.hibernate.Query;
import
net.sf.hibernate.Session;
import
net.sf.hibernate.SessionFactory;
import
net.sf.hibernate.Transaction;
import
net.sf.hibernate.cfg.Configuration;
public
class
FacadeEx2Bis {
private
SessionFactory sessFactory;
public
void
configure
(
) throws
HibernateException {
sessFactory =
new
Configuration
(
)
.addClass
(
utilisateur2.Connexion.class
)
.addClass
(
Personne.class
)
.addClass
(
Role.class
)
.addClass
(
Utilisateur2.class
)
.buildSessionFactory
(
);
System.out.println
(
"Configuration termine OK
\n
"
);
}
/* Lister les utilisateurs qui ont ete deja connectes
but : tester HQL
*/
public
TriString[] utilsConnectesHQL
(
) throws
HibernateException {
TriString[] tabTriString ;
ArrayList al=
new
ArrayList
(
);
String str1,str2,str3;
Integer cpt;
Session session =
sessFactory.openSession
(
);
Transaction tx =
null
;
try
{
tx =
session.beginTransaction
(
);
Iterator it1 =
session.iterate
(
"select pers.nom,pers.prenom,util.login,count(con) "
+
"from Personne as pers "
+
"join pers.utilisateur2s as util "
+
"join util.connexions as con "
+
" group by pers.nom, pers.prenom,util.login "
+
" having count(con)>0"
);
int
i=
0
;
//
while
(
it1.hasNext
(
)) {
Object[] tuple =
(
Object[]) it1.next
(
);
str1=(
String) tuple[0
];
str2=(
String) tuple[1
];
str3=(
String) tuple[2
];
cpt=(
Integer) tuple[3
];
al.add
(
i,new
TriString
(
str1,str2,str3,cpt));
i++
;
}
// On cree un tableau de la taille de l'it1 pour accueillir les elements
System.out.println (
"Nombre de lignes retournees : "
+
i);
tabTriString=
new
TriString[i];
// on remplit le tableau
for
(
int
j=
0
;j<
i;j++
)
{
tabTriString[j]=(
TriString) al.get
(
j);
}
tx.commit
(
);
}
catch
(
HibernateException he) {
if
(
tx!=
null
) tx.rollback
(
);
throw
he;
}
finally
{
session.close
(
);
}
return
tabTriString;
}
public
void
utilsConnectesCriteria
(
) throws
HibernateException {
TriString[] tabTriString ;
ArrayList al=
new
ArrayList
(
);
String str1,str2,str3;
Integer cpt;
Session session =
sessFactory.openSession
(
);
Transaction tx =
null
;
try
{
tx =
session.beginTransaction
(
);
List personnes=
session.createCriteria
(
Personne.class
)
.createCriteria
(
"utilisateur2s"
).createCriteria
(
"connexions"
)
.add
(
Expression.isNotNull
(
"idCnx"
))
.list
(
);
int
i=
0
;
i=
personnes.size
(
);
System.out.println
(
"Nom personne | prenom "
);
for
(
int
j=
0
;j<
i;j++
)
{
System.out.println
(((
Personne)personnes.get
(
j)).getNom
(
)+
" | "
+((
Personne)personnes.get
(
j)).getPrenom
(
));
}
tx.commit
(
);
}
catch
(
HibernateException he) {
if
(
tx!=
null
) tx.rollback
(
);
throw
he;
}
finally
{
session.close
(
);
}
//return tabTriString;
}
}
et la classe ClientEx2Bis.java modifiée :
package
utilisateur2;
import
utilisateur2.*;
import
net.sf.hibernate.HibernateException;
import
net.sf.hibernate.Query;
import
net.sf.hibernate.Session;
import
net.sf.hibernate.SessionFactory;
import
net.sf.hibernate.Transaction;
import
net.sf.hibernate.cfg.Configuration;
public
class
ClientEx2Bis {
public
static
void
main (
String argv[]) throws
HibernateException {
FacadeEx2Bis facadeEx2 =
new
FacadeEx2Bis
(
);
try
{
facadeEx2.configure
(
);
}
catch
(
HibernateException he1) {
System.out.println
(
"Error Hibernate "
+
he1.getMessage
(
)+
"
\n
"
);
he1.printStackTrace
(
);
}
TriString[] triStr=
facadeEx2.utilsConnectesHQL
(
);
// affichage des resultats dans la console :
System.out.println
(
"Personne.nom | Personne.prenom | Utilisateur2.login | nbConnexions"
);
for
(
int
i=
0
;i<
triStr.length;i++
) {
System.out.println
(
triStr[i].string1+
" | "
+
triStr[i].string2+
" | "
+
triStr[i].string3+
" | "
+
triStr[i].cpt.toString
(
));
}
// Utilisation API CRITERIA :
facadeEx2.utilsConnectesCriteria
(
);
}
}
Le lancement du client lanceEx2Bis.ksh donne le résultat suivant dans la console :
Nom personne |
prenom
EXTERNE0 |
JEAN-
LOUIS0
EXTERNE0 |
JEAN-
LOUIS0
EXTERNE0 |
JEAN-
LOUIS0
EXTERNE0 |
JEAN-
LOUIS0
EXTERNE0 |
JEAN-
LOUIS0
EXTERNE0 |
JEAN-
LOUIS0
EXTERNE0 |
JEAN-
LOUIS0
Correspondant aux sept connexions de la personne 5 (4 sous Utilisateur2 : 18 et 3 sous Utilisateur2 : 17).
À compléter pour les plus courageux. Je resterai pour ma part, sur les requêtes HQL, qui me semblent plus souples.
III-C-6. Pour aller plus loin avec Hibernate▲
Pour information, il semble que la norme EJB V3, fasse un virage important sur les sessions et Entity Beans qui deviendraient des POJO (suppression des homes interfaces, des fichiers de descriptions de déploiement…). La compatibilité ascendante serait assurée. Les tests de EJB V3 seraient faits à partir de deux logiciels de mapping O/R (Hibernate et TopLink) mais pas JDO !
Gavin King fait partie du groupe d'expert EJB V3…
Voir sur le thread http://www.theserverside.com/discussions/thread.tss?thread_id=25804#121153
Çà « bastonne » dur !
Nous avons au cours des exemples précédents effleuré Hibernate, nous avons laissé de côté les concepts suivants :
- aspect performance : avec les lazy initialisation (initialisation paresseuse => seulement quand on en a besoin) et les proxy ;
- utilisation et optimisation des caches ;
- utilisation des outils type XDoclet, MiddleGen ou AndroMDA ;
- Optimistic concurrency control (Versionning, timestamp…) et pessimist Locking
- et sûrement plein d'autres fonctionnalités.
Pour approfondir, lire la Documentation de référence, les FAQ et forum sous http://www.hibernate.org/.
tester et adopter !.
Pour le mois août 2004, est annoncée la sortie de :
HIBERNATE IN ACTION
August 2004, Softbound, 400 pages
ISBN 193239415X
Our price : $44.95
You can order this book from your bookstore by using the ISBN and title listed above.
Et voir aussi ce que va donner EJB V3 et la « guerre » EJB V3 (Hibernate/TopLink) vs JDO V2.0.
III-D. Partie Java Server Faces▲
III-D-1. Exemple simplifié▲
Nous allons repartir de l'exemple correspondant à la structure de la table l'utilisateur telle que définie ci-dessous dans la base Postgres.
essai=>
\d utilisateurs
Table «public
.utilisateurs»
Colonne |
Type |
Modificateurs
---------------+-----------------------------+---------------
iduser |
bigint |
not null
nom |
character varying
(
40
) |
not null
prenom |
character varying
(
40
) |
not null
password |
character varying
(
20
) |
not null
datecm |
timestamp without time zone |
issupprimable |
integer |
Index :
«cons_iduser» clé primaire, btree (
iduser)
Contraintes :
«utilisateurs_issupprimable» CHECK (
issupprimable =
0
OR issupprimable =
1
)
Ci-dessous le schéma rappelle l'exemple que l'on va traiter.
Les enregistrements de la table utilisateurs :
essai=>
select *
from utilisateurs;
iduser |
nom |
prenom |
password |
datecm |
issupprimable
--------+-----------+--------+-----------+----------------------------+---------------
1
|
admin |
admin |
admin |
2004
-
04
-
28
21
:34
:26.036093
|
0
25
|
Pasturel1 |
jl1 |
pasturel1 |
2004
-
05
-
07
21
:54
:22.961
|
1
26
|
Pasturel1 |
jl1 |
pasturel1 |
2004
-
05
-
07
22
:03
:03.054
|
1
27
|
Pasturel1 |
jl1 |
pasturel1 |
2004
-
05
-
07
22
:12
:16.629
|
1
29
|
Pasturel1 |
jl1 |
pasturel1 |
2004
-
05
-
07
22
:49
:18.387
|
1
30
|
Pasturel1 |
jl1 |
pasturel1 |
2004
-
05
-
07
22
:51
:08.809
|
1
24
|
Pasturel1 |
jl1 |
arheu123 |
2004
-
05
-
07
21
:53
:54.309
|
1
(
7
lignes)
essai=>
III-D-1-a. Mise en place de Hibernate et des Java Server Faces dans le bundle Jonas4-Tomcat5▲
À partir des maigres informations fournies par la documentation de référence de Hibernate, je donne ci-dessous une configuration permettant de faire fonctionner les exemples fournis dans le présent tutoriel.
Pour mémoire ci-dessous la hiérarchie des ClassLoader de Jonas tirée de la documentation de Jonas (jonas.objectweb.org)
The resulting JOnAS class loader hierarchy is as follows:
Nous avons donc plusieurs solutions pour positionner les .jar nécessaires au fonctionnement de l'implémentation de référence des Java Server Faces et de Hibernate. Nous allons essayer de rendre cette intégration (JSF + Hibernate) accessible au niveau le plus haut : c'est-à-dire le System ClassLoader ou Commons ClassLoader du schéma ci-dessus. La hiérarchie utilisée dans notre tutoriel (sans utilisation des EJB) est décrite par la flèche en gras sur la droite du schéma.
Nos applications dans les exemples du tutoriel sont des Web Application (sans EJB) qui utilisent Hibernate comme outil de persistance et mapping O/R.
Nous allons essayer d'utiliser le classloader global de Jonas pour intégrer JSF et Hibernate.
Le premier exemple simple va nous servir de test pour bien positionner les jar (car certains sont dans des versions différentes). On va remonter au plus haut les versions des .jar identiques à celles utilisées par Jonas. Sinon il faudra laisser des .jar au niveau du classLoader de la Web Application (répertoire WEB-INF/lib).
Nous allons donc positionner tous les jar nécessaires à JSF et Hibernate sous $JONAS_BASE/lib/ext.
Fichiers récupérés sous lib de Java Server Faces :
- demo-components.jar ;
- jsf-api.jar ;
- jstl.jar ;
- jsf-impl.jar ;
- standard.jar.
Fichiers récupérés de l'arborescence Hibernate2 :
- hibernate2.jar ;
- cglib-full-2.0.1.jar ;
- dom4j-1.4.jar ;
- ehcache-0.7.jar ;
- log4j-1.2.8.jar ;
- odmg-3.0.jar ;
- xalan-2.4.0.jar ;
- xerces-2.4.0.jar ;
- xml-apis.jar.
Fichier jdbc Driver Postgres :
- pg74.1jdbc3.jar.
Les fichiers jar de l'arborescence Hibernate déjà présents dans Jonas n'ont pas été recopiés, on utilise ceux fournis par Jonas.
Nous positionnons ensuite des fichiers de configuration pour Hibernate (récupérés de l'arborescence Hibernate) sous le répertoire $JONAS_BASE/conf :
- cache.ccf ;
- ehcache.xml ;
- log4j.properties ;
- treecache.xml.
Nota 1 :
Vous noterez que nous n'avons pas mis de fichier de configuration de Hibernate de nom :
hibernate.properties dans ces arborescences. Contrairement aux exemples antérieurs, nous utiliserons l'autre alternative de configuration de Hibernate à travers le fichier <webapps>.cfg.xml. J'ai positionné le fichier <webapps>.cfg.xml sous le répertoire $JONAS_BASE/conf avec une modification mineure de code pour charger cette configuration lors de l'utilisation de Hibernate (voir plus bas).
On pourrait le positionner ailleurs : par exemple sous l'arborescence WEB-INF, en indiquant le chemin de l'arborescence déployée (à savoir $JONAS_BASE/work/jonas/<webapps>/WEB-INF/<wepapps>.cfg.xml).
Nota 2 :
Je n'ai pas réussi à le faire fonctionner en essayant de lire le fichier de configuration .cfg.xml contenu dans l'archive .war. Je suis preneur de tip si quelqu'un trouve.
Si l'équipe de développement Jonas intègre JSF et/ou Hibernate dans une version future de Jonas, il est possible que les fichiers .jar soient positionnés sous le répertoire $JONAS_ROOT/lib directement et que l'équipe Jonas indique dans quel répertoire les fichiers .cfg.xml sont à positionner.
III-D-1-b. Adaptation du package la classe POJOUtilisateur▲
Nous allons ouvrir une nouvelle arborescence de fichiers pour gérer le packaging pour cette classe POJOUtilisateur.
L'organisation des fichiers nécessaires et suffisants des sources est la suivante :
Racine : fichiers de compilation ant et propriété de Hibernate
- ./ex3/build.xml ;
- ./ex3/build.properties.
Fichiers sources :
- ./ex3/src/utilisateur1/POJOUtilisateur1.java ;
- ./ex3/src/utilisateur1/BKUtilisateur1.java ;
- ./ex3/src/utilisateur1/BDUser.java ;
- ./ex3/src/utilisateur1/POJOUtilisateur1.hbm.xml ;
- ./ex3/src/utilisateur.cfg.xml.
Fichiers web / jsf :
- ./ex3/web/WEB-INF/web.xml ;
- ./ex3/web/WEB-INF/faces-config.xml ;
- ./ex3/web/success.jsp ;
- ./ex3/web/index.jsp ;
- ./ex3/web/saisieUtil.jsp.
Fichiers obtenus après avoir lancé ant :
- ./ex3/build/jsf-utilisateur/WEB-INF/faces-config.xml ;
- ./ex3/build/jsf-utilisateur/WEB-INF/web.xml ;
- ./ex3/build/jsf-utilisateur/saisieUtil.jsp ;
- ./ex3/build/jsf-utilisateur/success.jsp ;
- ./ex3/build/jsf-utilisateur/index.jsp.
La web Application obtenu :
- ./ex3/jsf-utilisateur.war.
Ant crée puis recopie le fichier jsf-utilisateur.war sous $JONAS_BASE/webapps et recopie le fichier utilisateur.cfg.xml sous $JONAS_BASE/conf.
III-D-1-c. Navigation simpliste de l'application▲
Le schéma ci-dessous montre la navigation simpliste utilisée dans notre application :
Les fichiers jsp, implémentant les Java Server Faces sont décrits ci-dessous et sont situés sous l'arborescence./ex3/src/web.
Le fichier d'accueil index.jsp qui ne fait qu'une redirection :
<!
doctype html public
"-//w3c//dtd html 4.0 transitional//en"
>
<
html>
<
head>
</
head>
<
body>
<
jsp:forward page=
"/jsf-utilisateur/saisieUtil.jsp"
/>
</
body>
</
html>
La page jsp/jsf de saisie d'un utilisateur saisieUtil.jsp :
<
html>
<
head>
<
title>
Application Demo Utilisateur</
title>
</
head>
<%
@
taglib uri=
"http://java.sun.com/jsf/html"
prefix=
"h"
%>
<%
@
taglib uri=
"http://java.sun.com/jsf/core"
prefix=
"f"
%>
<
body bgcolor=
"white"
>
<
f:view>
<
P ALIGN=
CENTER STYLE=
"border : 1.00pt solid #000000; padding : 0.05cm"
>
<
BR><
BR>
<
FONT SIZE=
5
><
B>
CREATION D'UN UTILISATEUR</B></FONT>
<
BR><
BR><
BR>
</
P>
<
h:form id=
"saisieUtil"
>
<
p>
<
h :outputText id=
"userNomOut"
value=
"Nom utilisateur"
/>
<
h :inputText id=
"userNomIn"
value=
"#{BKUtilisateur1.nom}"
/>
</
p>
<
p>
<
h :outputText id=
"userPrenomOut"
value=
"Prenom utilisateur"
/>
<
h :inputText id=
"userPrenomIn"
value=
"#{BKUtilisateur1.prenom}"
/>
</
p>
<
p>
<
h :outputText id=
"userPasswordOut"
value=
"Password Utilisateur"
/>
<
h :inputSecret id=
"userPasswordIn"
value=
"#{BKUtilisateur1.password}"
/>
</
p>
<
p>
<
h :outputText id=
"userIsModifOut"
value=
"Supprimable (1 = oui) :"
/>
<
h :inputText id=
"userIsModifIn"
value=
"#{BKUtilisateur1.isSupprimable}"
/>
</
p>
<
h :commandButton id=
"submit"
action=
"#{BKUtilisateur1.createUser}"
value=
"Submit"
/>
</
h:form>
</
f:view>
</
body>
</
HTML>
Noter les lignes :
<%
@
taglib uri=
"http://java.sun.com/jsf/html"
prefix=
"h"
%>
<%
@
taglib uri=
"http://java.sun.com/jsf/core"
prefix=
"f"
%>
incluant les bibliothèques de tag nécessaires aux jsf , la bibliothèque constituant le noyau qui sera préfixée par « f » et la bibliothèque de renderer html préfixée par la lettre « h ».
Ensuite la particularité des JSF est l'utilisation d'une Expression Langage (introduit par les JSP v2.0 => mais noter la subtilité d'identification du JSF EL commençant par # alors que le JSP EL commence par $) pour lier une action sur un composant de la page avec une Backing Bean chargé de gérer l'événement lié à l'action.
Dans notre cas le fait d'appuyer sur submit va déclencher la méthode createUser du Backing Bean BKUtilisateur1. Les valeurs saisies dans les champs des éléments sont liées aux attributs du Backing bean à travers l'attribut value du tag inputText et de l'EL correspondante de l'attribut value et permettent donc à l'aide d'un objet Business Delegate (BDUser) d'aller créer l'utilisateur en base.
La page jsp/jsf de congratulation success.jsp :
<
HTML>
<
HEAD>
<
title>
Bravo pour la Saisie !!</
title>
</
HEAD>
<%
@
taglib uri=
"http://java.sun.com/jsf/html"
prefix=
"h"
%>
<%
@
taglib uri=
"http://java.sun.com/jsf/core"
prefix=
"f"
%>
<
body bgcolor=
"white"
>
<
f:view>
<
h :form id=
"responseForm"
>
<
h2><
h :outputText id=
"result"
value=
"BRAVO !"
/></
h2>
<
h :commandButton id=
"back"
value=
"Back"
action=
"#{BKUtilisateur1.nettoieUser}"
/>
</
h:form>
</
f:view>
</
body>
</
HTML>
Rien de particulier par rapport aux mécanismes déjà décrits, si ce n'est que l'on profile de nettoyer les valeurs du Backing Bean lorsque l'on clique sur la touche « Back » à l'aide de la méthode nettoieUser.
Le Backing Bean BKUtilisateur1 constituant le modèle de la vue est donné ci-dessous :
package
utilisateur1;
import
javax.faces.component.UIComponent;
import
javax.faces.context.FacesContext;
import
javax.faces.validator.LongRangeValidator;
import
javax.faces.validator.Validator;
import
javax.faces.validator.ValidatorException;
public
class
BKUtilisateur1 {
private
String nom=
""
;
private
String prenom;
private
String password;
private
int
isSupprimable;
public
void
setNom (
String _nom)
{
nom=
_nom;
System.out.println
(
"Nom saisie : "
+
_nom);
}
public
String getNom
(
)
{
return
nom;
}
public
void
setPrenom (
String _prenom)
{
prenom=
_prenom;
}
public
String getPrenom
(
)
{
return
prenom;
}
public
void
setPassword (
String _password)
{
password=
_password;
}
public
String getPassword
(
)
{
return
password;
}
public
void
setIsSupprimable (
int
_isSup)
{
isSupprimable=
_isSup;
}
public
int
getIsSupprimable
(
)
{
return
isSupprimable;
}
public
String createUser
(
)
{
BDUser util=
new
BDUser
(
nom,prenom,password,isSupprimable);
util.createUser
(
);
return
(
"success"
);
}
public
String nettoieUser
(
)
{
setNom
(
""
);
setPrenom
(
""
);
setPassword
(
""
);
setIsSupprimable
(
0
);
return
(
"success"
);
}
}
On notera que le Backing Bean délègue la création du User en base au Business Delegate BDUser (voir méthode createUser dans le code ci-dessus).
Le fichier web.xml d'une web application classique, si ce n'est qu'il définit le servlet contrôleur des pages JSF, point d'entrée de toute application JSF.
<
?xml version=
'1.0'
encoding=
'UTF-8'
?>
<!
DOCTYPE web-
app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd"
>
<
web-
app>
<
display-
name>
JavaServer Faces Utilisateur Application</
display-
name>
<
description>
JavaServer Faces Utilisateur Application Application
</
description>
<
context-
param>
<
param-
name>
javax.faces.STATE_SAVING_METHOD</
param-
name>
<
param-
value>
client</
param-
value>
</
context-
param>
<
context-
param>
<
param-
name>
com.sun.faces.validateXml</
param-
name>
<
param-
value>
true
</
param-
value>
<
description>
Set this
flag to true
if
you want the JavaServer Faces
Reference Implementation to validate the XML in your
faces-
config.xml resources against the DTD. Default
value is false
.
</
description>
</
context-
param>
<
context-
param>
<
param-
name>
com.sun.faces.verifyObjects</
param-
name>
<
param-
value>
false
</
param-
value>
<
description>
Set this
flag to true
if
you want the JavaServer Faces
Reference Implementation to verify that all of the application
objects you have configured (
components, converters,
renderers, and validators) can be successfully created.
Default value is false
.
</
description>
</
context-
param>
<!--
Faces Servlet -->
<
servlet>
<
servlet-
name>
Faces Servlet</
servlet-
name>
<
servlet-
class
>
javax.faces.webapp.FacesServlet</
servlet-
class
>
<
load-
on-
startup>
1
</
load-
on-
startup>
</
servlet>
<!--
Faces Servlet Mapping -->
<
servlet-
mapping>
<
servlet-
name>
Faces Servlet</
servlet-
name>
<
url-
pattern>/
jsf-
utilisateur/*</url-pattern>
</servlet-mapping>
<security-constraint>
<!-- This security constraint illustrates how JSP pages
with JavaServer Faces components can be protected from
being accessed without going through the Faces Servlet.
The security constraint ensures that the Faces Servlet will
be used or the pages will not be processed. -->
<display-name>Restrict access to JSP pages</display-name>
<web-resource-collection>
<web-resource-name>
Restrict access to JSP pages
</web-resource-name>
<url-pattern>/saisieUtil.jsp</url-pattern>
<url-pattern>/success.jsp</url-pattern>
</web-resource-collection>
<auth-constraint>
<description>
With no roles defined, no access granted
</description>
</auth-constraint>
</security-constraint>
</web-app>
Noter le servlet contrôleur : javax.faces.webapp.FacesServlet
Et le fichier important faces-config.xml décrivant la navigation entre les différentes pages JSF.
<
?xml version=
'1.0'
encoding=
'UTF-8'
?>
<!
DOCTYPE faces-
config PUBLIC
"-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.1//EN"
"http://java.sun.com/dtd/web-facesconfig_1_1.dtd"
>
<
faces-
config>
<
application>
<
locale-
config>
<
default
-
locale>
en</
default
-
locale>
<
supported-
locale>
de</
supported-
locale>
<
supported-
locale>
fr</
supported-
locale>
<
supported-
locale>
es</
supported-
locale>
</
locale-
config>
</
application>
<
navigation-
rule>
<
description>
Navigation a partir de la page saisieUtil.jsp
</
description>
<
from-
view-
id>/
saisieUtil.jsp</
from-
view-
id>
<
navigation-
case
>
<
description>
Navigation a partir de la page saisieUtil.jsp en cas de retour "success"
.
</
description>
<
from-
outcome>
success</
from-
outcome>
<
to-
view-
id>/
success.jsp</
to-
view-
id>
</
navigation-
case
>
</
navigation-
rule>
<
navigation-
rule>
<
description>
Navigation a partir de la page success.jsp
</
description>
<
from-
view-
id>/
success.jsp</
from-
view-
id>
<
navigation-
case
>
<
description>
Navigation a partir de la page success.jsp en cas de retour "success"
.
</
description>
<
from-
outcome>
success</
from-
outcome>
<
to-
view-
id>/
saisieUtil.jsp</
to-
view-
id>
</
navigation-
case
>
</
navigation-
rule>
<
managed-
bean>
<
description>
Le "backing Bean"
BKUtilisateur1
</
description>
<
managed-
bean-
name>
BKUtilisateur1</
managed-
bean-
name>
<
managed-
bean-
class
>
utilisateur1.BKUtilisateur1</
managed-
bean-
class
>
<
managed-
bean-
scope>
session</
managed-
bean-
scope>
</
managed-
bean>
</
faces-
config>
Description facilement compréhensible des règles de navigation. On peut rajouter des branchements en cas d'échec par exemple et compléter ce modèle. Je laisse au lecteur le soin de faire l'exercice.
III-D-1-d. Les fichiers constituant la partie métier de la web application▲
Le premier élément en lien direct est le Business Delegate de notre petite application qui est porté par la classe BDUser.java ci-dessous :
package
utilisateur1;
import
net.sf.hibernate.*;
import
java.io.*;
import
net.sf.hibernate.cfg.*;
import
java.util.zip.*;
public
class
BDUser
{
private
String nom, prenom,password;
private
int
isSupprimable;
public
BDUser (
String _nom,String _prenom,String _password,int
_isSup)
{
nom=
_nom;
prenom=
_prenom;
password=
_password;
isSupprimable=
_isSup;
}
public
void
createUser
(
)
{
Transaction tx=
null
;
Session session=
null
;
try
{
// Nouveau avec fichier de conf Hibernate utilisateur.cfg.xml
SessionFactory sessionFactory=
new
Configuration
(
)
.configure
(
new
File
(
System.getProperty
(
"jonas.base"
)
+
File.separator+
"conf"
+
File.separator+
"utilisateur.cfg.xml"
))
.buildSessionFactory
(
);
if
(
sessionFactory ==
null
)
{
System.out.println (
"session Factory est);
}
// ouverture de la session
session =
sessionFactory.openSession
(
);
// Creation d'un user
tx =
session.beginTransaction
(
);
POJOUtilisateur1 util1=
new
POJOUtilisateur1
(
);
// on peuple l'objet util
// mais pas la clé primaire, qui nous sera retournée par session.save();
util1.setNom
(
this
.nom);
util1.setPrenom
(
this
.prenom);
util1.setPassword
(
this
.password);
util1.setDateCM
(
new
java.util.Date
(
));
util1.setIsSupprimable
(
this
.isSupprimable);
// on recupere la primary key
Long id=
(
Long) session.save
(
util1);
tx.commit
(
);
System.out.println
(
"Id sauvegarde = "
+
id.toString
(
));
}
catch
(
Exception e) {
try
{
if
(
tx!=
null
) tx.rollback
(
);
e.printStackTrace
(
);
}
catch
(
HibernateException he) {
he.printStackTrace
(
);
}
}
finally
{
try
{
session.close
(
);
}
catch
(
HibernateException he) {
he.printStackTrace
(
);
}
}
}
}
Ce Business Delegate pour utiliser la persistance O/R gérée par Hibernate à travers un fichier de configuration utilisateur.xml.cfg qui est placé sous $JONAS_BASE/conf comme le montre la ligne ci-dessous extraite du code ci-dessus :
SessionFactory sessionFactory=
new
Configuration
(
)
.configure
(
new
File
(
System.getProperty
(
"jonas.base"
)
+
File.separator+
"conf"
+
File.separator
+
"utilisateur.cfg.xml"
)).buildSessionFactory
(
);
Le fichier utilisateur.cfg.xml est donné ci-dessous :
<
?xml version=
'1.0'
encoding=
'utf-8'
?>
<!
DOCTYPE hibernate-
configuration
PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-2.0.dtd"
>
<
hibernate-
configuration>
<
session-
factory>
<
property name=
"connection.datasource"
>
jdbc_1</
property>
<
property name=
"connection.username"
>
pastjl</
property>
<
property name=
"connection.password"
></
property>
<
property name=
"show_sql"
>
true
</
property>
<
property name=
"dialect"
>
net.sf.hibernate.dialect.PostgreSQLDialect</
property>
<!--
Mapping files -->
<
mapping resource=
"utilisateur1/POJOUtilisateur1.hbm.xml"
/>
</
session-
factory>
</
hibernate-
configuration>
On notera que dans ce fichier de configuration, Hibernate recherche une datasource dont le nom est jdbc_1. Cette datasource est mise à disposition par Jonas. Elle est décrite dans le fichier $JONAS_BASE/conf/PostgreSQL1.properties dont le contenu est donné un peu plus bas.
À noter qu'il faut aussi fournir dans le fichier utilisateur.cfg.xml un nom d'utilisateur de la datasource et un password, Hibernate ne sait pas le lire dans la ressource jdbc_1 bien que ces paramètres soient positionnés.
Puis est défini dans ce fichier de configuration, le fichier de mapping O/R pour la classe POJOUtilisateur1 : POJOUtilisateur1.hbm.xml.
Le fichier Jonas $JONAS_BASE/conf/PostgreSQL1.properties définissant la ressource jdbc_1 :
###################### PostgreSQL DataSource configuration example
#
#####
# DataSource configuration
# Replace db_jonas and jonas by appropriate values.
#
datasource.name jdbc_1
datasource.url jdbc:postgresql:essai
datasource.classname org.postgresql.Driver
datasource.username pastjl
datasource.password
datasource.mapper rdb.postgres
#####
# ConnectionManager configuration
#
# JDBC connection checking level.
# 0
=
no special checking
# 1
=
check physical connection is still open
before reusing it
# 2
=
try
every connection before reusing it
jdbc.connchecklevel 1
# Max age for
jdbc connections
# nb of minutes a connection can be kept in the pool
jdbc.connmaxage 30
# Test statement
jdbc.connteststmt select 1
Ensuite le fichier de mapping O/R POJOUtilisateur1.hbm.xml :
<
?xml version=
"1.0"
?>
<!
DOCTYPE hibernate-
mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd"
>
<
hibernate-
mapping>
<
class
name=
"utilisateur1.POJOUtilisateur1"
table=
"utilisateurs"
>
<
id name=
"idUser"
type=
"long"
unsaved-
value=
"null"
column =
"iduser"
>
<
generator class
=
"sequence"
/>
</
id>
<
property name=
"nom"
type=
"string"
/>
<
property name=
"prenom"
type=
"string"
/>
<
property name=
"password"
type=
"string"
/>
<
property name=
"dateCM"
type=
"timestamp"
column=
"datecm"
/>
<
property name=
"isSupprimable"
type=
"integer"
column=
"issupprimable"
/>
</
class
>
</
hibernate-
mapping>
Déjà expliqué tout cela dans les chapitres précédents.
Et enfin la classe POJOUtilisateur1.java mappée par Hibernate vers la table Postgres utilisateurs :
package
utilisateur1;
public
class
POJOUtilisateur1 {
private
long
idUser;
private
String nom;
private
String prenom;
private
String password;
private
java.util.Date dateCM;
private
int
isSupprimable;
public
void
setIdUser (
long
_id)
{
idUser=
_id;
}
public
long
getIdUser
(
)
{
return
idUser;
}
public
void
setNom (
String _nom)
{
nom=
_nom;
}
public
String getNom
(
)
{
return
nom;
}
public
void
setPrenom (
String _prenom)
{
prenom=
_prenom;
}
public
String getPrenom
(
)
{
return
prenom;
}
public
void
setPassword (
String _password)
{
password=
_password;
}
public
String getPassword
(
)
{
return
password;
}
public
void
setDateCM (
java.util.Date _date)
{
dateCM=
_date;
}
public
java.util.Date getDateCM
(
)
{
return
dateCM;
}
public
void
setIsSupprimable (
int
_isSup)
{
isSupprimable=
_isSup;
}
public
int
getIsSupprimable
(
)
{
return
isSupprimable;
}
}
Cela aussi déjà expliqué plus haut.
Pour info un extrait du fichier $JONAS_BASE/conf/jonas.properties définissant la mise à disposition de la datasource postgres :
...
###################### JOnAS DBM Database service configuration
#
# Set the name of the implementation class
of the dbm service
jonas.service.dbm.class
org.objectweb.jonas.dbm.DataBaseServiceImpl
# Set the jonas DataSources. This enables the JOnAS server to load
# the data dources, to load related jdbc drivers, and to register the data
# sources into JNDI.
# This property is set with
a coma-
separated list of Datasource properties
# file names (
without the '.properties'
suffix).
# Ex: Oracle1,InstantDB1 (
while
the Datasources properties file names are
# Oracle1.properties and InstantDB1.properties)
jonas.service.dbm.datasources PostgreSQL1
...
Jonas cherche un fichier $JONAS_BASE/conf/PostgreSQL1.properties (décrit plus haut) pour mettre à disposition la datasource à travers le mécanisme JNDI.
Noter :
Le fait de pouvoir préciser par des noms différents (sous $JONAS_BASE/conf dans notre cas) les fichiers de configuration avec une syntaxe du type <webapps>.cfg.xml permet donc de déployer plusieurs webapps avec Hibernate sous Jonas.
Et voilà on a fait notre première application Jonas / JSF / Hibernate / Postgres !
IV. L'application en images▲
IV-A. Construction de l'application▲
Nous utiliserons pour construire l'application l'outil de gestion des compilations Ant.
Le fichier build.xml décrit les taches nécessaires à la compilation, à la constitution de l'archive jsf-utilisateur.war de la web application et à son déploiement dans le serveur Jonas/Tomcat.
Le fichier build.properties paramètre Ant dans notre contexte :
tomcat.home=/
opt/
JONAS_4_0_0
jonas.base=/
opt/
jonas_base
# The following property settings assume that you are using JWSDP 1.3
.
# If you are using any other environment, adjust the properties
# accordingly.
# -----
Dependant Properties -----
# The following properties do
not have to be configured if
you are
# using a JWSDP 1.3
conteneur.
# In a container other than JWSDP 1.3
, make sure that you have each
# of these dependancies and the properties point to the correct
# location :
jsf-
api.jar=
${
jonas.base}/
lib/
ext/
jsf-
api.jar
jsf-
impl.jar=
${
jonas.base}/
lib/
ext/
jsf-
impl.jar
commons-
logging.jar=
${
tomcat.home}/
lib/
common/
jakarta/
commons-
logging/
commons-
logging-
api.jar
commons-
digester.jar=
${
tomcat.home}/
lib/
common/
jakarta/
commons-
digester/
commons-
digester.jar
commons-
beanutils.jar=
${
tomcat.home}/
lib/
common/
jakarta/
commons-
beanutils/
commons-
beanutils.jar
commons-
collections.jar=
${
tomcat.home}/
lib/
common/
jakarta/
commons-
collections/
commons-
collections.jar
jstl.jar=
${
jonas.base}/
lib/
ext/
jstl.jar
standard.jar=
${
jonas.base}/
lib/
ext/
standard.jar
servlet.jar=
${
tomcat.home}/
lib/
common/
j2ee/
servlet/
servlet-
2_4.
jar
jsp.jar=
${
tomcat.home}/
lib/
common/
j2ee/
jsp/
jsp-
2_0.
jar
hibernate2.jar=
${
jonas.base}/
lib/
ext/
hibernate2.jar
# -----
Build Control Properties -----
# By default
, the dependent libraries are included with
# every sample app. Since the libraries come bundled with
# containers like JWSDP 1.3
you may want to use the container's
# libraries instead and not bundle them with
your web application.
# To not bundle the dependant libraries with
the sample apps
# comment out the following property:
build.standalone=
true
Il faut initialiser les paramètres tomcat.home avec la valeur de $JONAS_ROOT et jonas.base avec la valeur de $JONAS_BASE.
On aura réalisé auparavant les copies des .jar nécessaires (de hibernate et JSF) comme indiqué dans le chapitre précédent en adaptant les arborescences dans le fichier build.properties ci-dessus.
Le fichier build.xml est donné ci-dessous :
<
project name=
"utilisateur"
default
=
"build.war"
basedir=
"."
>
<
target name=
"init"
depends=
"verifyPreconditions"
>
<
tstamp/>
</
target>
<
property file=
"build.properties"
/>
<!--
this
demo local -->
<!--
Configure the context path for
this
application -->
<
property name=
"context.path"
value=
"/jsf-utilisateur"
/>
<
property name=
"example"
value=
"jsf-utilisateur"
/>
<
property name=
"build"
value=
"${basedir}/build"
/>
<
property name=
"web"
value=
"${basedir}/web"
/>
<
path id=
"classpath"
>
<
pathelement location=
"${commons-beanutils.jar}"
/>
<
pathelement location=
"${commons-collections.jar}"
/>
<
pathelement location=
"${commons-digester.jar}"
/>
<
pathelement location=
"${commons-logging.jar}"
/>
<
pathelement location=
"${jsf-api.jar}"
/>
<
pathelement location=
"${jsf-impl.jar}"
/>
<
pathelement location=
"${jstl.jar}"
/>
<
pathelement location=
"${build}/${example}/WEB-INF/classes"
/>
<
pathelement location=
"${servlet.jar}"
/>
<
pathelement location=
"${hibernate2.jar}"
/>
</
path>
<
target name=
"clean"
>
<
delete dir=
"${build}"
/>
<
delete dir=
"${context.path}"
/>
</
target>
<
target name=
"verifyPreconditions"
>
<
antcall target=
"checkRequiredFile"
>
<
param name=
"file"
value=
"${servlet.jar}"
/>
<
param name=
"fail.message"
value=
"A jar file containing the Servlet 2.3
and JSP1.2
classes is required to compile guessNumber.
Please define the property servlet.jar in your build.properties file
and ensure that the file exists."/>
</
antcall>
<
antcall target=
"checkRequiredFile"
>
<
param name=
"file"
value=
"${commons-beanutils.jar}"
/>
<
param name=
"fail.message"
value=
"${commons-beanutils.jar} must exist."
/>
</
antcall>
<
antcall target=
"checkRequiredFile"
>
<
param name=
"file"
value=
"${commons-digester.jar}"
/>
<
param name=
"fail.message"
value=
"${commons-digester.jar} must exist."
/>
</
antcall>
<
antcall target=
"checkRequiredFile"
>
<
param name=
"file"
value=
"${commons-logging.jar}"
/>
<
param name=
"fail.message"
value=
"${commons-logging.jar} must exist."
/>
</
antcall>
<
antcall target=
"checkRequiredFile"
>
<
param name=
"file"
value=
"${commons-collections.jar}"
/>
<
param name=
"fail.message"
value=
"${commons-collections.jar} must exist."
/>
</
antcall>
<
antcall target=
"checkRequiredFile"
>
<
param name=
"file"
value=
"${jsf-api.jar}"
/>
<
param name=
"fail.message"
value=
"${jsf-api.jar} must exist."
/>
</
antcall>
<
antcall target=
"checkRequiredFile"
>
<
param name=
"file"
value=
"${jsf-impl.jar}"
/>
<
param name=
"fail.message"
value=
"${jsf-impl.jar} must exist."
/>
</
antcall>
<
antcall target=
"checkRequiredFile"
>
<
param name=
"file"
value=
"${standard.jar}"
/>
<
param name=
"fail.message"
value=
"${standard.jar} must exist."
/>
</
antcall>
<
antcall target=
"checkRequiredFile"
>
<
param name=
"file"
value=
"${jstl.jar}"
/>
<
param name=
"fail.message"
value=
"${jstl.jar} must exist."
/>
</
antcall>
</
target>
<
target name=
"prepare"
depends=
"init"
description=
"Create build directories."
>
<
mkdir dir=
"${build}/${example}"
/>
<
mkdir dir=
"${build}/${example}/WEB-INF"
/>
<
mkdir dir=
"${build}/${example}/WEB-INF/classes"
/>
<
mkdir dir=
"${build}/${example}/WEB-INF/lib"
/>
</
target>
<!--
Executable Targets -->
<
target name=
"build"
depends=
"prepare,deploy.copyJars"
description=
"Compile Java files and copy static files."
>
<
javac srcdir=
"src"
destdir=
"${build}/${example}/WEB-INF/classes"
>
<
include name=
"**/*.java"
/>
<
classpath refid=
"classpath"
/>
</
javac>
<
copy todir=
"${build}/${example}/WEB-INF"
>
<
fileset dir=
"web/WEB-INF"
>
<
include name=
"*.xml"
/>
</
fileset>
</
copy>
<
copy todir=
"${build}/${example}/WEB-INF/classes/utilisateur1"
>
<
fileset dir=
"src/utilisateur1"
>
<
include name=
"*.xml"
/>
</
fileset>
</
copy>
<
copy todir=
"${build}/${example}/"
>
<
fileset dir=
"web"
>
<
include name=
"*.html"
/>
<
include name=
"*.gif"
/>
<
include name=
"*.jpg"
/>
<
include name=
"*.jsp"
/>
<
include name=
"*.xml"
/>
</
fileset>
</
copy>
</
target>
<
target name=
"deploy.copyJars"
if
=
"build.standalone"
>
</
target>
<
target name=
"checkRequiredFile"
>
<
available file=
"${file}"
property=
"requirement.satisfied"
/>
<
antcall target=
"checkRequired.fail"
/>
</
target>
<
target name=
"checkRequired.fail"
unless=
"requirement.satisfied"
>
<
fail message=
"${fail.message}"
/>
</
target>
<
target name=
"build.war"
depends=
"build"
>
<!--
create a war file for
distribution -->
<
jar jarfile=
"${example}.war"
basedir=
"${build}/${example}"
/>
<
copy todir=
"/opt/jonas_base/webapps"
file=
"${example}.war"
/>
<
copy todir=
"/opt/jonas_base/conf"
file=
"./src/utilisateur.cfg.xml"
/>
<!--
delete file=
"${example}.war"
/-->
</
target>
</
project>
Le fichier build.xml ci-dessus sert à la compilation et au déploiement de la web application. On adaptera donc ce fichier à sa configuration.
Ensuite pour créer l'application et la mettre en place sous le répertoire $JONAS_BASE/webapps de Jonas, il suffit de lancer la commande (sous le même répertoire que build.xml)
ant
IV-B. Déploiement de l'application dans Jonas▲
Après avoir lancé Postgesql, Jonas et tapé la l'URL http://localhost:9000/jonasAdmin dans votre navigateur préféré.
et ensuite l'écran de proposition de déploiement où l'on voit notre application jsf-utilisateur.war prête à déployer :
Après avoir basculé la web application dans le panneau de droite, cliqué sur Apply puis Confirm, nous avons déployé notre application prête à l'emploi :
On notera aussi dans la partie gauche la ressource database identifiée par PostgreSQL1 qui est donc disponible.
L'application est maintenant prête à être utilisée.
IV-C. Utilisation de l'application▲
Pour cela il suffit de rentrer l'URL mappée dans le fichier web.xml par rapport au servlet javax.faces.webapp.FacesServlet : http://localhost:9000/jsf-utilisateur.
L'interface est spartiate et mériterait des mises en forme css. Je n'ai pas utilisé ce mécanisme, mais JSF le permet.
État de la base avant la saisie :
essai=
# select *
from utilisateurs;
iduser |
nom |
prenom |
password |
datecm |
issupprimable
--------+-----------+--------+----------+----------------------------+---------------
1
|
admin |
admin |
admin |
2004
-
04
-
28
21
:34
:26.036093
|
0
24
|
Pasturel1 |
jl1 |
arheu123 |
2004
-
05
-
07
21
:53
:54.309
|
1
57
|
Pasturel2 |
jlp2 |
jlp2 |
2004
-
06
-
16
18
:11
:07.03
|
1
58
|
Pasturel3 |
jlp3 |
jlp3 |
2004
-
06
-
16
18
:11
:20.376
|
1
(
4
lignes)
Saisie d'un utilisateur :
Et après clic sur Submit, la congratulation :
Et après un clic sur Back le retour à l'écran précédent avec nettoyage des champs de saisie :
Et le résultat dans la base de données :
essai=
# select *
from utilisateurs;
iduser |
nom |
prenom |
password |
datecm |
issupprimable
--------+---------------+------------+----------+----------------------------+---------------
1
|
admin |
admin |
admin |
2004
-
04
-
28
21
:34
:26.036093
|
0
24
|
Pasturel1 |
jl1 |
arheu123 |
2004
-
05
-
07
21
:53
:54.309
|
1
57
|
Pasturel2 |
jlp2 |
jlp2 |
2004
-
06
-
16
18
:11
:07.03
|
1
58
|
Pasturel3 |
jlp3 |
jlp3 |
2004
-
06
-
16
18
:11
:20.376
|
1
79
|
PASTUREL_DEMO |
JEAN-
LOUIS |
jlp1 |
2004
-
06
-
22
18
:58
:51.69
|
1
(
5
lignes)
Et les traces Hibernate probatoires :
Hibernate
:
select nextval (
'hibernate_sequence'
)
Hibernate
:
insert into utilisateurs (
nom, prenom, password, datecm, issupprimable, iduser) values (
, ?, ?, ?, ?)
Id sauvegarde =
79
Remarque : les sauts de séquence sont dus à des essais infructueux et des effacements d'enregistrements…
Le portage vers Jonas 4.1/tomcat 5 ne devrait pas poser de problème, ainsi que l'adaptation au système Windows. Pour ma part j'ai réalisé ce portage sous Windows avec la base Postgres sous cygwin.
Bon courage pour aller plus loin…