Auteur    :    Stéphane TRAUMAT ( Ashita-studio )
Je veux �tre tenu au
courant des mises à jour
de ce tutorial
Date de dernière mise à jour    :    13/01/2003
Index du tutorial    :    http://www.ashita-studio.com/tutoriaux/ejb/index.php
Adresse du document    :    http://www.ashita-studio.com/tutoriaux/ejb/chapitre_07.php


Chapitre 07 : Un EJB entité - persistance gérée par le conteneur


  1. Introduction

    Nous avons appris que les EJB de type entité permettent de représenter les données persistantes de l'application. Nous allons maintenant voir qu'il y a, en fait, deux façons de gérer cette persistance.

    Dans le chapitre précédent, nous avons écrit les méthodes qui synchronisent les données membres de notre EJB avec notre base de données. C'est ce que l'on appelle la persistance gérée par le composant ( BMP : Bean Managed Persistance ).

    Au lieu d'écrire nous mêmes ces méthodes, nous pouvons laisser le conteneur se charger des appels à la base de données : c'est ce qu'on appelle la persistance gérée par le conteneur ( CMP : Container Managed Persistance ).

    Cette façon de faire permet :
    • De supprimer tout le code lié aux bases de données de votre source ( accès, requêtes et traitements ).
    • D'être indépendant de la base de données utilisée.

    Afin de bien vous montrer les différences entre les deux approches, nous allons reprendre l'exemple du chapitre précédent en laissant le conteneur gérer la persistance.


  2. La base de données

    La structure de la table CLIENTS et la façon de configurer la base de données sont les mêmes.


  3. Ecriture du composant

    1. Interface distante

      Identique à celle vue dans le chapitre précédent.

      Client.java
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      package crm_cmp;

      import javax.ejb.EJBObject;
      import java.rmi.RemoteException;

      public interface Client extends EJBObject 

        
      public String getNom() throws RemoteException;

        
      public String getPrenom() throws RemoteException;

        
      public void setPrenom(String prenomthrows RemoteException;

      }
      Java2html



    2. Interface locale

      Identique à celle vue dans le chapitre précédent.

      ClientHome.java
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      package crm_cmp;

      import java.io.Serializable;
      import java.rmi.RemoteException;
      import javax.ejb.*;
      import java.util.*;

      public interface ClientHome extends EJBHome 

        
      public Client create(String codeClient, String nom, String prenomthrows RemoteException, CreateException;

        
      public Client findByPrimaryKey(String codeClientthrows RemoteException, FinderException;

        
      public Collection findByNom(String nomthrows RemoteException, FinderException;

      Java2html

      Nous n'implémenterons pas le code de nos méthodes finder dans notre EJB car il sera généré par l'utilitaire GenIC. Nous aurons juste à spécifier la clause WHERE de la méthode findByNom() dans le descripteur de déploiement.


    3. L'Enterprise JavaBean

      ClientBean.java
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      package crm_cmp;

      import java.sql.*;
      import javax.sql.*;
      import java.util.*;
      import javax.ejb.*;
      import javax.naming.*;

      public class ClientBean implements EntityBean {

        
      // Données du client
        
      public  String  codeClient;
        
      public  String  nom;
        
      public  String  prenom;

        
      // Contexte de l'EJB
        
      private EntityContext context;

        
      // Fonction promise dans l'interface distante
        
      public String getNom() {
          
      return nom;
        
      }
        
      // Fonction promise dans l'interface distante
        
      public String getPrenom() {
          
      return prenom;
        
      }

         
      // Fonction promise dans l'interface distante
        
      public void setPrenom(String prenom) {
          
      this.prenom = prenom;
        
      }

        
      // Crée le composant et le sauvegarde dans la base de données avec un insert
        
      public String ejbCreate(String codeClient, String nom, String prenomthrows CreateException {
          
          
      // renseigne les propriétés du composant
          
      this.codeClient = codeClient;
          
      this.nom = nom;
          
      this.prenom = prenom;

          
      return null;
        
      }

        
      public void ejbPostCreate(String codeClient, String nom, String prenom) { 
          
      // Ne fait rien    
        


        
      // Cette fonction permet de supprimer un composant
        
      public void ejbRemove()  throws RemoveException {

        } 

        
      // Appelé juste après la création ou la réactivation du composant 
        //
        
      public void setEntityContext(EntityContext context) {
          
      this.context = context;
        
      }

        
      // Appellé lorsque le composant est enlevé
        
      public void unsetEntityContext() {
          
      this.context = null;
        
      }

        
      // appellé lorsque l'objet redevient actif
        
      public void ejbActivate() {

        }

        
      // appellé lorsque l'objet devient passif
        
      public void ejbPassivate() {

        }

        
      // Cette fonction est une fonction de synchronisation, elle est appellée par le conteneur 
        // lorsqu'il veut lire les informations de l'EJB depuis le support de stockage 
        
      public void ejbLoad() {

        }
         
        
      // Cette fonction est une fonction de synchronisation, elle est appellée par le 
        // conteneur lorsqu'il veut sauvegarder les informations de l'EJB dans le support de stockage
        
      public void ejbStore() {

        }

      }
      Java2html

      • ejbCreate()

        Etant donné que la persistance est gérée au niveau du conteneur, ce que retourne la méthode ejbCreate() est ignoré. C'est pour cette raison qu'elle retourne null.

        L'insertion effective des champs dans la base de données ne sera faite qu'après l'invocation de la méthode ejbCreate().


      • ejbRemove()

        La méthode ejbRemove() est appelée par le conteneur lorsqu'un client appelle la méthode remove(). Si vous avez besoin d'effectuer des traitements avant destruction, implémentez les ici.


      • ejbLoad()

        Le conteneur détermine le moment où il doit rafraîchir l'état de l'entité à partir de la base de données. Lorsque l'action devient nécessaire, le conteneur sélectionne la ligne voulue dans la base, il assigne les valeurs des colonnes de cette donnée aux variables d'instance, puis il invoque ejbLoad().


      • ejbStore()

        Lorsque le conteneur détermine qu'il est nécessaire de synchroniser la base de données avec l'état courant du composant, il commence par invoquer la méthode ejbStore(), puis il met à jour la base de données avec les valeurs des variables d'instance de l'EJB.





  4. Création du descripteur de déploiement de notre EJB

    1. Descripteur de déploiement standard

      Par rapport à notre précédent descripteur de déploiement, la différence se situe au niveau de persistance-type. Comme vous pouvez le constater, nous avons mis comme valeur Container ce qui, bien sur, défini que notre persistance sera gérée par le conteneur.

      Nous avons aussi déclaré toutes nos variables d'instance comme cmp-field, elles seront donc tous stockées dans la base de données.

      Puis, avec la balise primkey-field, on définit quel sera le champs qui sera la clé primaire.

      ejb-jar.xml

      <?xml version="1.0" encoding="ISO-8859-1"?>
      <!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN" "http://java.sun.com/dtd/ejb-jar_2_0.dtd">

      <ejb-jar>
        <description>Déscripteur de déploiement pour l'EJB client</description>
        <display-name>EJB Client</display-name>
        <enterprise-beans>
            <entity>
            <description>EJB Client ( CMP )</description>
            <ejb-name>Client</ejb-name>
            <home>crm_cmp.ClientHome</home>
            <remote>crm_cmp.Client</remote>
            <ejb-class>crm_cmp.ClientBean</ejb-class>
            <persistence-type>Container</persistence-type>
            <prim-key-class>java.lang.String</prim-key-class>
            <reentrant>False</reentrant>
            <cmp-field>
              <field-name>codeClient</field-name>
            </cmp-field>
            <cmp-field>
              <field-name>nom</field-name>
            </cmp-field>
            <cmp-field>
              <field-name>prenom</field-name>
            </cmp-field>
            <primkey-field>codeClient</primkey-field>
            </entity>
        </enterprise-beans>

        <assembly-descriptor>
          <container-transaction>
            <method>
              <ejb-name>Client</ejb-name>
              <method-name>*</method-name>
            </method>
            <trans-attribute>Required</trans-attribute>
          </container-transaction>
        </assembly-descriptor>

      </ejb-jar>


    2. Descripteur de déploiement spécifique à JOnAS

      Ce descripteur de déploiement, en plus de définir le nom JNDI, va définir :

      • Quelle connexion le conteneur doit utiliser pour se connecter à la base de données ( conMySQL_CRM ).

      • Le nom de la table dans laquelle doivent être stockées les valeurs des variables d'instance de notre EJB ( CLIENTS ).

      • Quel champs de la table CLIENTS doit acceuilir la valeur de la variable d'instance codeClient ( CODE_CLIENT ).

      • Quel champs de la table CLIENTS doit acceuilir la valeur de la variable d'instance nom ( NOM ).

      • Quel champs de la table CLIENTS doit acceuilir la valeur de la variable d'instance prenom ( PRENOM ).

      • Que la méthode findByNom() retourne les EJB correspondant au résultat d'une requête sur la table CLIENTS dont la clause where est 'where NOM = ?'.

      jonas-ejb-jar.xml

      <?xml version="1.0" encoding="ISO-8859-1"?>
      <!DOCTYPE jonas-ejb-jar PUBLIC "-//ObjectWeb//DTD JOnAS 2.4//EN" "http://www.objectweb.org/jonas/dtds/jonas-ejb-jar_2_4.dtd">

      <jonas-ejb-jar>
        <jonas-entity>
          <ejb-name>Client</ejb-name>
            <jndi-name>MyClient</jndi-name>
            <jdbc-mapping>
              <jndi-name>conMySQL_CRM</jndi-name>
              <jdbc-table-name>CLIENTS</jdbc-table-name>
              <cmp-field-jdbc-mapping>
                <field-name>codeClient</field-name>
                <jdbc-field-name>CODE_CLIENT</jdbc-field-name>
              </cmp-field-jdbc-mapping>
              <cmp-field-jdbc-mapping>
                <field-name>nom</field-name>
                <jdbc-field-name>NOM</jdbc-field-name>
              </cmp-field-jdbc-mapping>
              <cmp-field-jdbc-mapping>
                <field-name>prenom</field-name>
                <jdbc-field-name>PRENOM</jdbc-field-name>
              </cmp-field-jdbc-mapping>
              <finder-method-jdbc-mapping>
                <jonas-method>
                  <method-name>findByNom</method-name>
                </jonas-method>
                  <jdbc-where-clause>where NOM = ?</jdbc-where-clause>
              </finder-method-jdbc-mapping>
            </jdbc-mapping>
        </jonas-entity>
      </jonas-ejb-jar>



  5. Packaging de notre EJB

    Aucune différence avec ce que l'on a vu dans les chapitres précédents. Voir le script fourni avec les sources.


  6. Déploiement de l'EJB

    Aucune différence avec ce que l'on a vu dans les chapitres précédents. Voir le script fourni avec les sources.


  7. Création du client de l'EJB

    Le code du client de notre EJB est identique à celui du précédent chapitre. En effet, seuls les méthodes qui permettent l'interaction entre l'Enterprise JavaBean et notre base données ont été modifiées et elles ne sont jamais appelées directement par le client.


  8. Réalisation de la compilation, du packaging et du déploiement

    Pour réaliser la compilation, le packaging et le déploiement, nous allons utilisé le script build.bat que vous trouverez dans le répertoire "C:\java\dev\Client_CMP".

    Ce script effectuera la compilation, le packaging et le déploiement de notre ejb et de notre client.


  9. Test de notre EJB

    Déployons notre EJB et testons le avec notre application.

    • cd C:\java\dev\Client_CMP

    • build

    • jonas start

    • cd C:\java\dev\build

    • jclient crm_cmp.Test

    Voici le résultat :

    Nom du client LGUEVARRA : GUEVARRA
    Liste des clients qui ont pour nom TRAUMAT :
    ->Stephane
    ->Pierrre

    Si on fait un select sur notre table CLIENTS, on obtient le résultat suivant :

    CODE_CLIENT NOM PRENOM
    STRAUMAT  TRAUMAT  Stephane 
    LGUEVARRA  GUEVARRA  Loreen 
    PTRAUMAT  TRAUMAT  Pierre 

    On voit bien que les insertions, les selections, les suppressions et les mises à jour se sont bien faites.


  10. Conclusion

    Comme vous avez pu le constater, la persistance gérée par le conteneur permet :
    • De supprimer tout le code lié aux bases de données de votre source (accès, requêtes et traitements).
    • De ne plus avoir à gérer les relations entre les EJB entités.
    • D'être indépendant de la base de données utilisée.
    • De réduire le nombre de lignes de codes (et donc de réduire le nombre de bug).
    • De réduire la complexité de vos applications.

    Bien entendu, tous ces avantages ont une contre-partie : les performances. Il faut donc les utiliser avec beaucoup de précautions.



Auteur : Stéphane TRAUMAT
Société SCUB et Ashita-studio
Valid HTML 4.01! Valid CSS!