WOW !! MUCH LOVE ! SO WORLD PEACE !
Fond bitcoin pour l'amélioration du site: 1memzGeKS7CB3ECNkzSn2qHwxU6NZoJ8o
  Dogecoin (tips/pourboires): DCLoo9Dd4qECqpMLurdgGnaoqbftj16Nvp


Home | Publier un mémoire | Une page au hasard

 > 

Rapport de Projet J2EE: site de e-commerce

( Télécharger le fichier original )
par Olivier Ferran
 - Bac+5 2008
  

précédent sommaire suivant

Bitcoin is a swarm of cyber hornets serving the goddess of wisdom, feeding on the fire of truth, exponentially growing ever smarter, faster, and stronger behind a wall of encrypted energy

4. La couche Web

La couche web est composée de tout le coté html de l'application, c'est-à-dire les Jsp, les feuilles de style css, le javascript, ainsi que des contrôles nécessaires à charger le modèle de chaque page à afficher.

a. JSTL

Le but de la JSTL est de simplifier le travail des auteurs de page JSP, c'est à dire la personne responsable de la couche présentation d'une application web J2EE. En effet, un web designer peut avoir des problèmes pour la conception de pages JSP du fait qu'il est confronté à un langage de script complexe qu'il ne maîtrise pas forcément.

La JSTL permet de développer des pages JSP en utilisant des balises XML, donc avec une syntaxe proche des langages utilisés par les web designers, et leur permet donc de concevoir des pages dynamiques complexes sans connaissances du langage Java.

Sun a donc proposé une spécification pour une librairie de tags standards : la Java Standard Tag Library (JSTL). C'est à dire qu'il spécifie les bases de cette librairie, mais qu'il laisse l'implémentation libre (de la même manière que pour les serveurs J2EE qui sont des implémentations de la spécification J2EE).

La JSTL se base sur l'utilisation des Expressions Languages (EL) en remplacement des scriptlets Java. L'exemple suivant tiré de la page de commandes permet de se rendre compte de la puissance des Expressions Languages :

<c:forEach var="item" varStatus="iter" items="${facture.pannier.contenu}">

...

<td>${item.portable.stockage.taille}Go (${item.portable.stockage.rotation}tr/min) </td>

...

</c:forEach>

Tout d'abord, on remarque qu'une EL commence par un « $ '> et que le contenu de l'expression doit se trouver a l'intérieur d'accolades. La page JSP va chercher un attribut s'appelant "item" successivement et dans l'ordre dans :

1 l'objet « request > qui représente la requête transmise par le contrôleur : request.getAttribute("item")

1 l'objet « session > qui représente la session du client : session.getAttribute("item")

1 l'objet « application > qui représente le contexte de l'application web :
application.getAttribute("item")

Puisque l'expression fait référence a un attribut composé, JSTL va chercher un membre de l'objet « item > qui s'appellerait « portable > (un membre public, ou accessible par un getter), puis va faire de même en cherchant un « membre '> stockage du type de l'objet « portable >, puis de même avec « taille '>. Une fois l'objet d'extrémité trouvé, la JSP l'affiche en utilisant la méthode « toString > de cet objet. Ainsi, on aura parcouru toute l'arborescence de l'objet très simplement, sans surcharge de code.

Dans le code précédent, on peut également observer l'utilisation d'un tag <c :forEach>.Ce tag fait partie d'une des quatre taglibs que propose JSTL, la taglib « Core > (les trois autres étant Format, XML, SQL), et propose de nombreuses actions de base tels que l'affichage de variables, la création ou la modification de variables de scope, la gestion d'exceptions, etc ~

Une taglib intéressante que nous avons également testée est la taglib Format (fmt) qui permet de simplifier la localisation et le formatage des données.

La première utilisation que nous en avons faite concerne la gestion des dates, dans notre module de news. En effet, les dates étant enregistrées dans notre base de données de façon non formatée, il nous faut les traiter avant leur affichage. Pour cela, la taglig Format propose deux tags :

<fmt:parseDate value="${news.date}" pattern="yyyy-MM-dd" var="date"/> <fmt:formatDate value="${date}" dateStyle="full"/>

Le premier permet de construire le modèle d'affichage de la date, en utilisant un pattern personnalisé. Il prend en paramètre la date à parser, le pattern à suivre, et le nom de la variable dans laquelle il placera le résultat.

Le tag « formatDate '> n'as plus alors qu'à afficher le résultat contenu dans la variable « date >.

Mais la principale utilité de cette taglib réside dans sa faculté d'internationaliser une application, c'est-à-dire d'afficher indifféremment tous les messages contenus sur un site dans différentes langues. Nous avons mis en place ce concept sur les premières étapes d'une commande.

La JSTL utilise les classes standards de Java pour la gestion de l'internationalisation. Ainsi la classe java.util.Locale permet de représenter les spécificités régionales, ainsi que la classe java.util.ResourceBundle pour accéder aux données des fichiers de localisation.

Un ResourceBundle permet de gérer un ensemble de fichier *.properties contenant les ressources localisées. Par exemple pour gérer les langues françaises et anglaises, nous avons les fichiers suivants:

1 messages_fr_FR.properties

1 messages_en_US.properties

pannierVide=Il n'y a pas d'items dans votre pannier quantite=Quantite

prixTTC=Prix TTC

Alors que le fichier « message_en_US.properties » contient pour sa part :

pannierVide=There is no item in your cart quantite=Quantity

prixTTC=IAT Price

Dès lors, l'utilisation est très simple puisqu'il suffit de choisir à un moment quelle est la langue à utiliser et de placer cette locale dans un objet de la session :

String langue = request.getParameter("langue");

if (langue != null){

if (langue.equals("FR"))

request.getSession().setAttribute("langue","fr_FR"); if (langue.equals("EN"))

request.getSession().setAttribute("langue","en_US");

}

Puis de rajouter dans les jsp concernées les deux lignes suivantes:

<fmt:setLocale value="${sessionScope.langue}"/>

<fmt:setBundle basename="messages" var="maLocale" scope="session"/>

Le tag « setLocale ~ permet d'aller chercher dans le scope session la variable représentant la langue a utiliser, celle que nous lui avons donné précédemment, et le tag setBundle spécifie le nom du fichier de base contenant les messages (on ajoute « basename »+ « locale » pour construire « messages_fr_FR.properties »).

Il ne suffit plus alors qu'à accéder a nos messages en utilisant le tag « message » en spécifiant la clé voulue.

<fmt:message bundle="${maLocale}" key="pannierVide"/>

1 Les fichiers *.properties comportent un ensemble de couples clef/valeur contenant l'ensemble des messages possibles a afficher. On accède aux données localisées grâce aux différentes clefs. Par exemple, le fichier « messages_fr_FR.properties » contient :

Rapport de Projet J2EE Site de E-Commerce

janv. 8

16

Pour avoir mis en place ce mécanisme, nous nous sommes rendus compte de la puissance de la JSTL, et notamment de la taglib Format car en à peine plus d'une demi heure, il est possible de rendre son site complètement multilingue, ce qui crée une forte valeur ajoutée à une application.

Il est également possible de créer sa propre bibliothèque de tags. Nous avons également testé cette possibilité en créant plusieurs tags que nous avons réunis dans une customTag. Par exemple, nous avons créé un tag nommé PromoTag dont le résultat est représenté par la capture suivante.

La construction d'un tel tag se fait en deux étapes. Tout d'abord, il faut décrire les paramètres de ce tag dans une tld.

<tag>

<name>promo</name> <tag-class>yaz.ecommerce.taglib.PromoTag</tag-class> <bodycontent>scriptless</bodycontent>

<description>

Utilisé pour afficher une offre spéciale

</description>

<attribute>

<name>image</name>

<required>true</required>

<rtexprvalue>false</rtexprvalue> </attribute>

<attribute>

<name>titre</name>

<required>true</required>

<rtexprvalue>false</rtexprvalue> </attribute>

</tag>

On y trouve le nom du tag, le chemin vers la classe qui correspond au code du tag, un paramètre spécifiant si le tag accepte ou nom des expression language ainsi que la liste complete des attributs du tag.

L'écriture du code du tag est très simple, il sufit de créer une classe héritant de javax.servlet.jsp.tagext.SimpleTagSupport, contenant un membre par propriété spécifiée avec ses setters puis de se servir du l'objet JSPWriter qui représente le flux de sortie de la jsp pour écrire le code html du tag.

public void doTag() throws JspException, IOException{ JspWriter writer = getJspContext().getOut(); writer.println ("<div class=\"produit1\">"); writer.println (" <div class=\"caracteristiques\">"); writer.println (" <div class=\"photo\"><img

src=\"images/"+image+"\" alt=\""+altImage+"\"/></div>");

writer.println (" <div class=\"description\">");

writer.println (" <h1>"+titre+"</h1>");

Même si dans notre cas, les tags que nous avons créés ont plus une visée pédagogique que pratique, on peut quand même entr'apercevoir de nombreuses utilisations de ce mécanisme. Par exemple, les taglibs de Struts sont construites de cette façon.

b. Ajax

De toutes les technologies en vogue en ce moment, Ajax est sans doute l'une des plus prometteuses. Nous n'avons pas dérogé à cette mode et avons testé son implémentation dans un site en java. Puisqu'étant déjà familiers du concept et de son implémentation en php, la retranscription en java est assez rapide.

Nous avons placé notre requête après l'affichage des résultats d'une recherche de produits, afin d'ajouter un produit au panier.

janv. 8

Rapport de Projet J2EE Site de E-Commerce

18

Dans un premier temps (1), l'utilisateur demande la page /recherche. Le conteneur de servlet donne la main au contrôleur pour traiter la demande et renvoie finalement le code html de la page désiré que le navigateur web affiche au client (2). Alors que cette première partie de l'échange est terminé, l'utilisateur va déclencher une fonction javascript qui, via une requête http sur une url bien précise, constituera la demande d'ajout d'un item dans son pannier (3). Le serveur traitera cette requête http et retournera non pas une page à afficher, mais du code javascript (4) que le navigateur pourra exécuter.

Dans notre cas, quand l'utilisateur clique sur le lien pour ajouter un objet a son panier, il déclenche en fond une requête vers l'uri AddItemAjax avec deux paramètres : l'identifiant du produit et la quantité à ajouter.

function doAjouterPannier(id, qte){

var xhr = getXHR();

if (xhr){

// true : requête asynchrone

xhr.open( "POST",

"./addItemAjax?portable="+id+"&quantite="+qte, true);

xhr.onreadystatechange = function() {
if (xhr.readyState == 4){

eval(xhr.responseText);

}

}

// envoie la requete au serveur xhr.send(null);

}

}

Coté serveur, une fois la requête reçue les paramètres récupérés, on se contente d'exécuter les fonctions nécessaires a l'ajout du produit au panier et on retourne quelques lignes de javascript que le client exécutera pour signifier a l'utilisateur que sa requête s'est déroulée avec succès en écrivant dans le flux de sortie de la servlet.

/* ajoute le portable au panier */ pannier.ajouterProduit(idPortable, quantite);

try{

PrintWriter pw = response.getWriter();

pw.println("var p = document.getElementById('ajoutPannierResponse"+idPortable+"');");

pw.println("p.innerHTML = '(" + quantite + " Article ajout&eacute; au pannier)'");

}

catch(IOException ex){

ex.printStackTrace();

}

5. La couche service

a. Synchronisation

La couche service est une couche clé de l'application puisque c'est par elle que transitent les informations entre la couche contrôleur et la couche d'accès aux données (dao). Son existence provient d'un constat simple : il est nécessaire de protéger la couche dao de possibles accès concurrentiels sur la base de données.

Considérons la situation suivante. Imaginons que nous possédions dans notre module de news une partie administration qui permettrait d'éditer le contenu d'une news, ou de la supprimer complètement. Imaginons maintenant que nous avons deux administrateurs qui tentent de modifier la même news en même temps. On sent bien qu'il peut arriver un problème entre le moment oi la première personne commence l'exécution de la fonction d'édition et le moment ou l'exécution se termine. Si la news a été supprimée par le deuxième administrateur entre temps, le programme va tenter de modifier un objet qui n'existe plus.

Pour résoudre ce problème, on a besoin de synchroniser l'accès aux méthodes de la couche dao afin de s'assurer qu'un seul thread les exécute a la fois. Pour cela, les classes du package dao ne sont instanciées qu'un un seul exemplaire. C'est ce que l'on appelle des « singletons ». Ces singletons sont répartis sur plusieurs classes, chacune d'entre elle gérant une seule table en base de données, ce qui rend possible un tel découpage.

Puisque pendant le déroulement normal de l'application, il existe de nombreux threads lancés en même temps et potentiellement concurrents, il faut trouver un moment oi il n'y a qu'un seul thread de lancé pour instancier nos objets dao. Cet instant a lieu pendant la phase d'initialisation de la servlet, lors de l'appel a la méthode init.

Chacune de nos classes dao est alors instanciée une seule fois et la référence ainsi créée est stockée en mémoire dans un objet accessible depuis le « scope application ».

/* construit une hashmap qui va contenir toutes les instances de la couche service */

HashMap<String,Object> mapService = new HashMap<String,Object>();

/* met la hashmap dans le scope de l'application */ getServletContext().setAttribute("mapService", mapService);

Rapport de Projet J2EE Site de E-Commerce

janv. 8

Nous mettons ainsi dans cette HashMap tous nos objets de la couche service dont les méthodes synchronisées permettent d'accéder a la couche dao.

public class LivraisonServiceImpl implements ILivraisonService{

private ILivraisonDao dao;

public LivraisonServiceImpl(){}

public synchronized void ajouterLivraison(Livraison livraison){ dao.ajouterLivraison(livraison);

}

public synchronized ArrayList<Livraison> getAllLivraisons(Integer id){ return dao.getAllLivraisons(id);

}

public synchronized Livraison getDefaultLivraison(Integer id){ return dao.getDefaultLivraison(id);

}

public ILivraisonDao getDao() { return dao;

}

public void setDao(ILivraisonDao dao) { this.dao = dao;

}

}

b. Le framework Spring

Lorsque nous ajoutons des objets dans la HashMap précédente, ce sont toujours des objets implémentant une interface contenant le prototype des méthodes proposées.

Considérons la classe LivraisonServiceImpl en la modifiant comme suit :

public LivraisonServiceImpl(){

dao = new LivraisonDaoImpl();

}

20

Si nous avions écrit ceci, notre classe LivraisonServiceImpl aurait été dépendante de la classe LivraisonDaoImpl puisque pour utiliser une autre classe implémentant l'interface ILivraisonDao, nous aurions du modifier l'objet a la main et recompiler la classe. Pour remédier à ce problème, nous avons choisi d'utiliser le framework Spring notamment pour ses fonctions d'Inversion of Control (Injection de Dépendance en français).

Spring IoC nous permet de créer une application 3tier où les couches sont indépendantes des autres, c'est a dire que changer l'une ne nécessite pas de changer les autres. Cela apporte une grande souplesse dans l'évolution de l'application.

Avec Spring IoC, la classe LivraisonServiceImpl va obtenir la référence dont il a besoin sur la couche dao de la façon suivante :

2.

1. Dès que besoin, nous demandons à Spring IoC de nous donner une référence vers un objet implémentant l'interface ILivraisonService.

Spring va alors exploiter un fichier XML de configuration qui lui indique quelle classe doit être instanciée et comment elle doit être initialisée.

3. Spring IoC nous rends alors la référence de la couche ainsi créée.

L'avantage de cette solution est que désormais le nom des classes instanciant les différentes couches n'est plus codé en dur mais simplement présent dans un fichier de configuration. Changer l'implémentation d'une couche induira un changement dans ce fichier de configuration mais pas dans les classes appelantes.

L'instanciation des beans lors de l'initialisation se fait dès lors de la façon suivante :

/* charge toutes les instances de la couche service dans la hashmap */ mapService.put("LivraisonServiceImpl",

(ILivraisonService)new XmlBeanFactory(

new ClassPathResource("spring-config.xml")) .getBean("livraison-service"));

La configuration de Spring s'effectue dans le fichier spring-config.xml de la façon suivante :

<bean id="livraison-dao"

class="yaz.ecommerce.dao.LivraisonDaoImpl" init-method="init"

destroy-method="close"/>

<bean id="livraison-service" class="yaz.ecommerce.service.LivraisonServiceImpl"> <property name="dao">

<ref local="livraison-dao" />

</property>

</bean>

Le premier bean définit un objet de type LivraisonDapImpl. C'est l'objet que nous allons passer a comme référence de la couche dao à la couche service. Les attributs « init-method » et « destroy-method » spécifient respectivement la méthode à appeler à la création du bean et à sa destruction.

Le deuxième bean est celui de notre couche service. Nous lui donnons la classe à instancier et définissons comment valoriser le membre « dao » en lui disant de prendre le bean local créé juste avant, dont l'id est « livraison-dao ». De cette façon, nous obtenons automatiquement un objet java prêt a l'emploi dont la configuration est aisément modifiable par simple modification d'un fichier xml.

6. La couche d'accès aux données (DAO)

a. La persistance des objets en via Object/Relational Mapping (ORM)

Pour faire court, la notion d'ORM est le fait d'automatiser la persistance des objets java dans les tables d'une base de données relationnelle, ainsi que son contraire, récupérer les données des tables directement dans des objets java.

Rapport de Projet J2EE Site de E-Commerce

janv. 8

Pour implémenter ce concept, un framework doit être composé des quatre éléments suivants :

22

( Une api pour effectuer les opérations CRUD (Create, Retrieve, Update, Delete) de base sur les objets des classes persistantes

( Un langage ou une api pour créer des requêtes qui se réfèrent aux classes et à leurs propriétés.

( Un moyen simple de spécifier les métadonnées pour le mapping

( L'implémentation de concepts tels que l'automatic dirty checking (mise à jour automatique des tables quand les objets java correspondants sont modifiés), le lazy loading (chargement des propriétés des objets uniquement des qu'ils sont accédés) ainsi que d'autres fonctions d'optimisation.

b. Hibernate

Parmi de nombreuses solutions répondant à cette attente (EJB, iBatis, JDO, Cayenne, Castor, etc..), nous avons choisi d'utiliser le framework Hibernate car c'est a l'heure actuelle le framework le plus puissant, le plus complet, et surtout le plus utilisé dans le monde professionnel.

Néanmoins, son utilisation est assez complexe malgré le grand nombre de documentations disponibles sur internet. Dans notre cas, nous nous sommes contenter d'utiliser les fonctions de bases nécessaires pour la persistance de nos données, sans nous préoccuper de toute la problématique concernant les performances.

Tout d'abord, avant d'utiliser Hibernate, il est nécessaire de modéliser les données que l'on va vouloir stocker. Cette étape consiste à construire des objets java dont les membres seront porteurs des informations à stocker. Chaque membre doit posséder un getter et un setter.

public class Commande {

private Integer id;

private Date dateCommande; private Date datePreparation; private Date dateExpedition; private Integer client;

private Livraison livraison; private Float port;

private Float tva;

public Commande(){}

/* getters, setters */

}

On peut noter en regardant cette simple entité qu'outre les types primitifs (ou les wrappers), il est également possible de mapper des types complexes (des objets de la classe Livraison). De plus, Hibernate est non intrusif, c'est-à-dire qu'il ne nécessite pas l'implémentation d'une quelconque interface ou héritage.

La deuxième étape consiste à mapper nos entités dans les tables de la base de données. Pour cela, Hibernate utilise deux types de fichiers de configuration.

<hibernate-configuration>

<session-factory>

<property name="hibernate.dialect">

org.hibernate.dialect.PostgreSQLDialect

</property>

<property name="hibernate.connection.driver_class"> org.postgresql.Driver

</property>

<property name="hibernate.connection.url"> jdbc:postgresql://localhost/e-commerce

</property>

<property name="hibernate.connection.username">

Postgres

</property>

<property name="hibernate.connection.password">

Administrateur

</property>

<property name="show_sql">true</property>

<property name="hibernate.jdbc.batch_size">0</property>

<mapping resource="yaz/ecommerce/entite/commande.hbm.xml"/> </session-factory>

</hibernate-configuration>

La propriété hibernate.dialect sert à configurer le dialecte SQL de la base de données. Ce dialecte va servir à Hibernate pour optimiser certaines parties de l'exécution en utilisant les propriétés spécifiques à la base. Cela se révèle très utile pour la génération de clé primaire et la gestion de concurrence. Chaque base de données à son dialecte propre.

La propriété hibernate.connection.driver_class permet de renseigner quel est le driver jdbc à utiliser. Ce driver est fournit par l'éditeur de la base de données.

La propriété hibernate.connection.url renseigne sur l'adresse de la base de données.

Les propriétés hibernate.connection.username et hibernate.connection.pasword correspondent aux identifiants du compte avec lequel Hibernate va accéder à la base de données.

Les deux propriétés suivantes sont optionnelles, le première permettant d'afficher les requêtes crées par Hibernate dans la console au moment de l'exécution, et la deuxième désactive les traitements par batch.

Ensuite, il faut ajouter tous les mappings des entités dont nous avons besoin grâce aux « balises mapping ressource ». En général, chaque objet persistant possède son fichier de mapping.

Le contenu d'un tel fichier pour la classe Commande est le suivant :

Le premier, hibernate.cfg.xml, regroupe les paramètres ayant trait à la base de données elle-même (son type, comment y accéder, etc...).

<hibernate-mapping package="yaz.ecommerce.entite">

<class name="Commande" table="commandes">

<id name="id" type="integer">

<generator class="sequence">

<param name="sequence">commandes_id_seq</param> </generator>

</id>

<property name="dateCommande" column="date_commande"

type="date" />

<property name="datePreparation" column="date_preparation" type="date" />

<property name="dateExpedition" column="date_expedition" type="date" />

<property name="client" type="integer" />

<component name="livraison"

class="yaz.ecommerce.entite.Livraison">

<property name="civilite" column="livraison_civilite" type="string" />

<property name="nom" column="livraison_nom" type="string"

/>

<property name="prenom" column="livraison_prenom"

type="string" />

<property name="adresse" column="livraison_adresse"

type="string" />

<property name="cp" column="livraison_cp" type="integer"

/>

<property name="ville" column="livraison_ville" type="string" />

</component>

<property name="port" type="float" /> <property name="tva" type="float" /> </class>

</hibernate-mapping>

La balise class définit le mapping de la classe Commande (l'attribut package sert de raccourci pour le package de cette classe) et l'attribut « table » donne la table dans la base de donnée qui va être concernée par cette classe.

On doit ensuite mapper tous les champs de la classe un par un, avec certaines spécificités.

Les classes mappées doivent déclarer la clé primaire de la table en base de données. Toutes nos classes ont ainsi une propriété présentant l'identifiant unique d'une instance. L'élément <id> sert à définir le mapping entre cette propriété et la clef primaire en base. L'élément fils <generator> nomme une classe Java utilisée pour générer les identifiants uniques pour les instances des classes persistantes. Certaines applications peuvent proposer leurs propres implémentations spécialisées pour le générateur de clés mais pour notre utilisation, nous nous servons d'une des implémentations intégrées à Hibernate. Il est bien sûr également possible d'utiliser des clés composées en mappant plusieurs attributs de la classe comme propriétés identifiantes mais nous ne les avons pas utilisées.

Toutes les propriétés simples se mappent aisément en spécifiant d'un coté le nom de la propriété dans la classe java et de l'autre le nom de la colonne dans la base de données.

Pour les propriétés complexes, on utilise l'élément <component> en lui passant en paramètre le nom de la propriété java dans l'objet et le chemin vers la classe correspondant a cet objet. Les paramètres sont alors mappés de la même façon que pour la classe englobante.

L'utilisation que nous avons faite d'Hibernate est assez rudimentaire puisque globalement, tous nos objets sont mappés de cette façon. Néanmoins, après avoir commencé la lecture du livre Hibernate in Action, nous nous sommes aperçus que la meilleure façon de modéliser les classes était différente de la notre, et qu'elle impliquait de nombreux autres éléments dans les fichiers de configuration.

24

Ainsi, même si notre mapping est fonctionnel, nous nous sommes rendu compte sur le tard de ses limites lorsque nous avons eu besoin de mapper des objets plus complexes qu'une simple classe contenant uniquement des types simples. Ce fut notamment le cas pour mapper les objets représentant les commandes, puisque ceux-ci comportaient des tableaux d'objets complexes représentant le contenu de la commande.

Néanmoins, c'est une fois le mapping effectué que l'on se rend compte de la puissance d'Hibernate. Pour manipuler les objets persistants, tout passe par l'objet Hibernate Session.

public void enregistrerCommande(

Integer idClient,

Livraison livraison,

Double tva,

Double port,

ArrayList<PannierItem> listePortables){

Transaction tx = null;

(1) session = sessionFactory.openSession();

Commande commande = new Commande(idClient, livraison,

25.0f , 19.6f);

try {

(2) tx = session.beginTransaction();

(3) session.save(commande);

for (Iterator it = listePortables.iterator(); it.hasNext();){

CommandeContenu contenu =

new CommandeContenu((PannierItem)it.next()); contenu.setCommande(commande.getId()); session.save(contenu);

}

(4) tx.commit();

}

catch (HibernateException e) {

e.printStackTrace();

if (tx != null && tx.isActive())

(5) tx.rollback();

}

finally {

session.close();

sessionFactory.close();

}

}

Tout d'abord, on récupère une session Hibernate (1) grâce a une fabrique qui analyse les fichiers xml de configuration et qui retourne un objet prêt a l'emploi. Ensuite, on débute une transaction (2), qui représente une unité de travail, c'est-à-dire toutes les instructions à réaliser pour compléter une tâche. En (3), nous constatons qu'il suffit d'une simple ligne pour insérer notre objet java dans la base de données ! En (4), nous flushons la session et terminons notre unité de travail. Il est remarquable de voir que si une exception s'est produite pendante la durée de la transaction, l'api Hibernate est capable d'annuler toutes les modifications qui se sont déroulé durant cette période (5).

Dès lors, on se rend bien compte des raisons qui ont menés au succès d'Hibernate par ce simple exemple.

tx = session.beginTransaction();

Commande commande =

(Commande)session.get(yaz.ecommerce.entite.Commande.class,id);

tx.commit();

Un simple appel à la fonction « get » avec en paramètre la classe de l'objet persistant et la clé primaire de l'objet en base permet de récupérer une instance persistance java. De même que si l'on utilise le code suivant durant une session:

tx = session.beginTransaction();

Commande commande =

(Commande)session.get(yaz.ecommerce.entite.Commande.class,id) Commande.setPort(12f);

tx.commit();

L'api Hibernate va détecter le changement d'une des propriétés de l'objet commande et va mettre a jour automatiquement l'enregistrement correspondant en base de données.

En ce qui concerne le requêtage, Hibernate est aussi bien capable d'utiliser le langage SQL

tx = session.beginTransaction();

Query q = session.createQuery("select * from commandes_contenu where

commande=:id");

q.setInteger("id",id);

contenu = (ArrayList<CommandeContenu>)q.list();

tx.commit();

Que le langage HQL (Hibernate Query Language). Ce dialecte orienté objet a été développé au

dessus du langage SQL afin de récupérer des objets en base de données (le R de CRUD).

tx = session.beginTransaction();

Query q = session.createQuery("from Commande as c where c.client =

?").setInteger(0, id);

liste = q.list();

tx.commit();

La différence entre les deux précédentes requêtes est qu'en HQL, la requête retourne une liste non pas d'Object mais de Commande.

Hibernate propose même un autre type de requêtage, via l'api Query By Criteria (QBC) qui permet de construire ses requêtes en spécifiant les contraintes dynamiquement sans manipulation de chaines de caractères comme on serait obligé de le faire en HQL ou en SQL.

Criteria crit = session.createCriteria(yaz.ecommerce.entite.News.class);

crit.addOrder(Order.desc("date")); crit.setMaxResults(10);

liste = (ArrayList)crit.list();

Rapport de Projet J2EE Site de E-Commerce

janv. 8

De la même façon, pour récupérer un objet persistant, on utilisera la fonction suivante :

26

précédent sommaire suivant






Bitcoin is a swarm of cyber hornets serving the goddess of wisdom, feeding on the fire of truth, exponentially growing ever smarter, faster, and stronger behind a wall of encrypted energy








"Ceux qui vivent sont ceux qui luttent"   Victor Hugo