Réalisation d'un héritage avec des EJB Entiy CMP

Cet article propose d'étudier l'implémentation du système d'héritage avec des EJB Entity de type CMP

Article lu   fois.

L'auteur

Site personnel

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

Introduction

La technologie de persistance objets J2EE, les Entity, a beaucoup évolué depuis sa création. Il reste pourtant toujours une des bases de la théorie objet qui n'est pas couverte : l'héritage. Cette faiblesse est souvent un argument utilisé par les détracteurs des EJB Entity. Ce besoin est pourtant plus un confort des technologies objets qu'une nécessité. Mais, comme avec l'ensemble des technologies J2EE, un bon design patterns nous rendra accessible cette fonctionnalité.

1. Problématique

Voici un exemple de modélisation que nous aimerions faire persister en base de données avec les EJB CMP:

Image non disponible

Sur ce diagramme de classe UML, il y a deux classes, une première classe personne qui contient les attributs id (identification), nom et prénom. Et une seconde classe salarié qui hérite de la classe personne et qui ajoute l'attribut salaire. Le salarié est une personne, le salaire est un attribut qu'un salarié possède, mais qu'une personne ne possède pas forcément, l'héritage se justifie donc ici. La méthode toString() a été ajoutée pour des raisons techniques.

Image non disponible

Voici un exemple d'utilisation de ces classes, TOTO est une personne, il a comme attributs : id=1, nom=TOTO, prénom=Toto. Et BOB qui est un salarié id=2, nom=BOB, prénom=Robert et salaire=12000.

Pour faire persister ces informations dans une base de données, nous allons utiliser un modèle merisien MPD (Modèle physique de données):

Image non disponible

La table PERSONNE contient les mêmes champs que les attributs de la classe Personne, id est utilisé pour être la clé primaire. La table SALARIE a la particularité de contenir une clé primaire qui est également clé étrangère (PK/FK).
Voici les même données dans les deux tables:

Image non disponible
Image non disponible

1.1. Script SQL

 
Sélectionnez

CREATE TABLE PERSONNE (
  ID                   INTEGER NOT NULL,
  NOM                  VARCHAR(100),
  PRENOM               VARCHAR(100),
  CONSTRAINT PK_PERSONNE PRIMARY KEY (ID)
);

CREATE TABLE SALARIE (
  ID                   INTEGER NOT NULL,
  SALAIRE              DOUBLE,
  CONSTRAINT PK_SALARIE PRIMARY KEY (ID)
);

ALTER TABLE SALARIE
   ADD CONSTRAINT FK_SALARIE_PERSONNE FOREIGN KEY (ID)
      REFERENCES PERSONNE (ID);

2. Modélisation

Pour arriver à ce résultat, nous allons étudier cette modélisation:

Image non disponible

De gauche à droite, nous trouvons:

  • Le client Programme console, web ou autres...
  • Le serveur J2EE avec un EJB session et deux Entity
  • La base de données où nous retrouvont nos deux tables

Ce diagramme représente une utilisation classique des Design Patterns J2EE, Façade & Value Object (la classe Personne et Salarié) aussi appelé DTO (Data Transfer Object).
Si vous n'êtes pas familiarisé avec ces deux Design Patterns, je vous recommande, pour la Façade, la lecture de l'article Design Patterns J2EE et Design Patterns UML. La technique du Value Object est, quand à elle, décrite dans l'article Design Patterns J2EE.

L'idée dans cette architecture est de faire bénéficier au client d'une réelle modélisation objet avec l'héritage que nous avons conçu précédemment. Les classes Personne et Salarié seront de simples classes Java que le développeur (le client) pourra utiliser sans aucune contrainte. Ces classes serviront aussi au transport d'informations entre le client et le serveur grâce à la sérialisation Java.
Un EJB session se chargera de faire le travail désiré : création, modification, recherche et suppression des objets. Il déléguera les traitements à la base de données en utilisant les EJB Entity adéquates.

3. Réalisation des objets sérialisés

3.1. Personne

Comme prévu, la classe personne est complètement neutre, on peut noter son support de la sérialisation avec le mot clé Serializable. Tous les attributs sont de la forme objet et non pas natif Integer plutôt que int pour éviter les problèmes de null de la base.

 
Sélectionnez

package com.dvp.heritage;

import java.io.Serializable;

public class Personne implements Serializable {
  private Integer id;
  private String nom;
  private String prenom;

  public Personne() {
  }

  public Personne(Integer id, String nom, String prenom) {
    this.id = id;
    this.nom = nom;
    this.prenom = prenom;
  }

  public Integer getId() {
    return id;
  }

  public String getNom() {
    return nom;
  }

  public String getPrenom() {
    return prenom;
  }

  public void setId(Integer id) {
    this.id = id;
  }

  public void setNom(String nom) {
    this.nom = nom;
  }

  public void setPrenom(String prenom) {
    this.prenom = prenom;
  }

  public String toString() {
    return return "Type=" + this.getClass().getName() + " id=" + id + " nom=" +
        nom + " prenom=" + prenom;
  }
}

3.2. Salarie

L'implémentation est identique à la classe Personne, nous pouvons constater le lien d'héritage avec la classe ancêtre avec le mot clé extends.

 
Sélectionnez

package com.dvp.heritage;

public class Salarie extends Personne {
  private Double salaire;

  public Salarie() {
    super();
  }
  public Salarie(Integer id, String nom, String prenom, Double salaire) {
    super(id, nom, prenom);
    this.salaire=salaire;
  }
  public Double getSalaire() {
    return salaire;
  }
  public void setSalaire(Double salaire) {
    this.salaire = salaire;
  }
  public String toString() {
    return super.toString()+" salaire="+salaire;
  }
}

4. Réalisation de l'EJB session (Facade)

L'EJB session est utilisé dans cet exemple comme une façade de tous les appels concernant les objets de type Personne (et donc Salarie). Ses interfaces sont donc déclarées en Remote pour les appels distants.
Les services suivants sont disponibles:

  • Find Recherche une personne (ou un salarié)
  • Create Création d'une personne (ou d'un salarié)
  • Update Modification d'une personne (ou d'un salarié)
  • Delete Suppression d'une personne (ou d'un salarié)
Image non disponible

4.1. Interface Remote: PersonneFacade

Rien de spécial pour cette classe.

 
Sélectionnez

package com.dvp.heritage.ejb;

import java.rmi.RemoteException;
import javax.ejb.EJBObject;
import com.dvp.heritage.Personne;

public interface PersonneFacade extends EJBObject {
  public Personne find(Integer id) throws RemoteException;
  public void create(Personne personne) throws RemoteException;
  public void delete(Integer id) throws RemoteException;
  public void update(Personne personne) throws RemoteException;
}

4.2. Implémentation Bean: PersonneBean

Voici la classe principale du projet, la facade, tout le travail lui revient.

Il y a deux choses à observer lors de l'accès aux Entity:
La première chose, c'est la chaîne de référence utilisée dans le lookup. Nous pouvons remarquer l'accès via "java:comp/env/ejb", j'ai configuré dans les "ejb-local-ref" de l'EJB façade les références sur les deux Entity. Cette manipulation permet de résoudre la localisation des Entity dès le démarrage du serveur d'application. Ce type de paramètrage évite les localisations des objets via le JNDI.

Image non disponible

La seconde chose à remarque est l'absence des mots clés PortableRemoteObject.narrow.Les deux CMP sont accédés par leur interface Local et non par leur Remote. Cette optimisation permet de faire des accès directs de classe à classe sans passer par les couches réseaux ou autres. Ici, le cas est idéal, les Entity ne seront accédés que par la façade.

Les implémentations font appel aux Entity en fonction du type d'instance Personne envoyé.

La dernière chose à remarquer, est la gestion des exceptions, ici, toutes les erreurs conduisent à une EJBException. Ce type de fonctionnement provoque une annulation de transaction, c'est ce qui est souhaitable ici, mais provoque aussi l'invalidation de l'EJB Session pour rien. Une meilleure implémentation utilisera des exceptions métiers pour aiguiller le cas d'une exception normale ou pas.

C'est bon? Je n'ai pas perdu trop de lecteur ici? ;o)

 
Sélectionnez

package com.dvp.heritage.ejb;

import javax.ejb.CreateException;
import javax.ejb.EJBException;
import javax.ejb.ObjectNotFoundException;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import com.dvp.heritage.Personne;
import com.dvp.heritage.Salarie;

public class PersonneFacadeBean implements SessionBean {
  SessionContext sessionContext;

  public static PersonneCMPHome getPersonneCMPHome() throws NamingException {
    Context ctx = new InitialContext();
    return (PersonneCMPHome) ctx.lookup(
        "java:comp/env/ejb/com/dvp/heritage/PersonneCMP");
  }

  public static SalarieCMPHome getSalaireCMPHome() throws NamingException {
    Context ctx = new InitialContext();
    return (SalarieCMPHome) ctx.lookup(
        "java:comp/env/ejb/com/dvp/heritage/SalarieCMP");
  }

  public void ejbCreate() throws CreateException {
  }

  public void ejbRemove() {
  }

  public void ejbActivate() {
  }

  public void ejbPassivate() {
  }

  public void setSessionContext(SessionContext sessionContext) {
    this.sessionContext = sessionContext;
  }

  public com.dvp.heritage.Personne find(Integer id) {
    try {
      PersonneCMP personneCMP = getPersonneCMPHome().findByPrimaryKey(id);
      try {
        SalarieCMP salarieCMP = getSalaireCMPHome().findByPrimaryKey(id);
        Salarie salarie = new Salarie(id, personneCMP.getNom(),
                                      personneCMP.getPrenom(),
                                      salarieCMP.getSalaire());
        return salarie;
      }
      catch (ObjectNotFoundException ex) {
        return new Personne(id, personneCMP.getNom(),
                            personneCMP.getPrenom());
      }
    }
    catch (Exception ex) {
      //Peux mieux faire...
      throw new EJBException(ex);
    }
  }

  public void create(Personne personne) {
    try {
      PersonneCMP personneCMP = getPersonneCMPHome().create(personne.getId(),
          personne.getNom(), personne.getPrenom());

      if (personne instanceof Salarie) {
        Salarie salarie = (Salarie) personne;
        SalarieCMP salarieCMP = getSalaireCMPHome().create(personne.getId(),
            salarie.getSalaire());
      }
    }
    catch (Exception ex) {
      //Peux mieux faire...
      throw new EJBException(ex);
    }
  }

  public void delete(Integer id) {
    try {
      PersonneCMP personneCMP = getPersonneCMPHome().findByPrimaryKey(id);
      try {
        SalarieCMP salarieCMP = getSalaireCMPHome().findByPrimaryKey(id);
        salarieCMP.remove();
      }
      catch (ObjectNotFoundException ex) {
      }
      personneCMP.remove();
    }
    catch (Exception ex) {
      //Peux mieux faire...
      throw new EJBException(ex);
    }
  }

  public void update(Personne personne) {
    try {
      PersonneCMP personneCMP = getPersonneCMPHome().findByPrimaryKey(personne.
          getId());
      personneCMP.setNom(personne.getNom());
      personneCMP.setPrenom(personne.getPrenom());
      if (personne instanceof Salarie) {
        Salarie salarie = (Salarie) personne;
        SalarieCMP salarieCMP = getSalaireCMPHome().findByPrimaryKey(personne.
            getId());
        salarieCMP.setSalaire(salarie.getSalaire());
      }
    }
    catch (Exception ex) {
      //Peux mieux faire...
      throw new EJBException(ex);
    }
  }
}

4.3. Interface Home: PersonneHome

Rien de spécial pour cette classe.

 
Sélectionnez

package com.dvp.heritage.ejb;

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

public interface PersonneFacadeHome extends javax.ejb.EJBHome {
  public PersonneFacade create() throws CreateException, RemoteException;
}

4.4. Descripteur de déploiement

On peut remarquer le système transactionel positionné à Required et la définition des deux références locales aux CMP.

 
Sélectionnez
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar>
    <enterprise-beans>
        <session>
            <display-name>PersonneFacade</display-name>
            <ejb-name>PersonneFacade</ejb-name>
            <home>com.dvp.heritage.ejb.PersonneFacadeHome</home>
            <remote>com.dvp.heritage.ejb.PersonneFacade</remote>
            <ejb-class>com.dvp.heritage.ejb.PersonneFacadeBean</ejb-class>
            <session-type>Stateless</session-type>
            <transaction-type>Container</transaction-type>
            <ejb-local-ref>
                <description />
                <ejb-ref-name>ejb/com/dvp/heritage/PersonneCMP</ejb-ref-name>
                <ejb-ref-type>Entity</ejb-ref-type>
                <local-home>com.dvp.heritage.ejb.PersonneCMPHome</local-home>
                <local>com.dvp.heritage.ejb.PersonneCMP</local>
                <ejb-link>PersonneCMP</ejb-link>
            </ejb-local-ref>
            <ejb-local-ref>
                <description />
                <ejb-ref-name>ejb/com/dvp/heritage/SalarieCMP</ejb-ref-name>
                <ejb-ref-type>Entity</ejb-ref-type>
                <local-home>com.dvp.heritage.ejb.SalarieCMPHome</local-home>
                <local>com.dvp.heritage.ejb.SalarieCMP</local>
                <ejb-link>SalarieCMP</ejb-link>
            </ejb-local-ref>
        </session>
    </enterprise-beans>
    <assembly-descriptor>
        <container-transaction>
            <method>
                <ejb-name>PersonneFacade</ejb-name>
                <method-name>*</method-name>
            </method>
            <trans-attribute>Required</trans-attribute>
        </container-transaction>
    </assembly-descriptor>
</ejb-jar>

5. Réalisation des EJB Entity CMP

Les deux EJB CMP sont utilisés pour la persistance des données, un lien CMR unidirectionnel a été créé pour la navigation de Salarié vers Personne. Ces deux EJB sont déclarés en local, seul l'EJB session pourra accèder à ces deux objets.

Image non disponible

5.1. Interface Locale: PersonneCMP

Définition des attributs de Personne.

 
Sélectionnez

package com.dvp.heritage.ejb;

import javax.ejb.EJBLocalObject;

public interface PersonneCMP extends EJBLocalObject {
  public Integer getId();
  public void setNom(String nom);
  public String getNom();
  public void setPrenom(String prenom);
  public String getPrenom();
}

5.2. Implémentation Bean: PersonneCMPBean

Rien de spécial pour cette implémentation.

 
Sélectionnez

package com.dvp.heritage.ejb;

import javax.ejb.CreateException;
import javax.ejb.EntityBean;
import javax.ejb.EntityContext;
import javax.ejb.RemoveException;

abstract public class PersonneCMPBean implements EntityBean {
  EntityContext entityContext;
  public Integer ejbCreate(Integer id, String nom, String prenom) throws
      CreateException {
    setId(id);
    setNom(nom);
    setPrenom(prenom);
    return null;
  }

  public void ejbPostCreate(Integer id, String nom, String prenom) throws
      CreateException {
  }

  public void ejbRemove() throws RemoveException {
  }

  public abstract void setId(Integer id);

  public abstract void setNom(String nom);

  public abstract void setPrenom(String prenom);

  public abstract Integer getId();

  public abstract String getNom();

  public abstract String getPrenom();

  public void ejbLoad() {
  }

  public void ejbStore() {
  }

  public void ejbActivate() {
  }

  public void ejbPassivate() {
  }

  public void unsetEntityContext() {
    this.entityContext = null;
  }

  public void setEntityContext(EntityContext entityContext) {
    this.entityContext = entityContext;
  }
}

5.3. Interface Home: PersonneCMPHome

Rien de spécial pour cette implémentation.

 
Sélectionnez

package com.dvp.heritage.ejb;

import javax.ejb.CreateException;
import javax.ejb.FinderException;

public interface PersonneCMPHome extends javax.ejb.EJBLocalHome {
  public PersonneCMP create(Integer id, String nom, String prenom) throws
      CreateException;

  public PersonneCMP findByPrimaryKey(Integer id) throws FinderException;
}

5.4. Interface Locale: SalarieCMP

Définition des attributs de Salarie uniquement, on peut remarquer ici les accesseurs à la classe Personne.

 
Sélectionnez

package com.dvp.heritage.ejb;

import javax.ejb.EJBLocalObject;

public interface SalarieCMP extends EJBLocalObject {
  public Integer getId();
  public void setSalaire(Double salaire);
  public Double getSalaire();
  public void setPersonneCMP(PersonneCMP personneCMP);
  public PersonneCMP getPersonneCMP();
}

5.5. Implémentation Bean: SalarieCMPBean

Rien de spécial pour cette implémentation. Là aussi, on retrouve les accesseurs à la classe Personne.

 
Sélectionnez

package com.dvp.heritage.ejb;

import javax.ejb.CreateException;
import javax.ejb.EntityBean;
import javax.ejb.EntityContext;
import javax.ejb.RemoveException;

abstract public class SalarieCMPBean implements EntityBean {
  EntityContext entityContext;
  public Integer ejbCreate(Integer id, Double salaire) throws CreateException {
    setId(id);
    setSalaire(salaire);
    return null;
  }
  public void ejbPostCreate(Integer id, Double salaire) throws CreateException {
  }
  public void ejbRemove() throws RemoveException {
  }
  public abstract void setId(Integer id);
  public abstract void setSalaire(Double salaire);
  public abstract void setPersonneCMP(PersonneCMP personneCMP);
  public abstract Integer getId();
  public abstract Double getSalaire();
  public abstract PersonneCMP getPersonneCMP();
  public void ejbLoad() {
  }
  public void ejbStore() {
  }
  public void ejbActivate() {
  }
  public void ejbPassivate() {
  }
  public void unsetEntityContext() {
    this.entityContext = null;
  }
  public void setEntityContext(EntityContext entityContext) {
    this.entityContext = entityContext;
  }
}

5.6. Interface Home: SalarieCMPHome

Rien de spécial pour cette implémentation.

 
Sélectionnez

package com.dvp.heritage.ejb;

import javax.ejb.CreateException;
import javax.ejb.FinderException;
import javax.ejb.EJBLocalHome;

public interface SalarieCMPHome extends EJBLocalHome {
  public SalarieCMP create(Integer id, Double salaire) throws CreateException;
  public SalarieCMP findByPrimaryKey(Integer id) throws FinderException;
}

5.7. Descripteur de déploiement

On peu remarquer le système transactionel positionné à Required, la définition locale des CMP et la relation CMR entre les deux Entity.

 
Sélectionnez
<?xml version="1.0" encoding="UTF-8"?>
<!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>
  <enterprise-beans>
    <entity>
      <display-name>PersonneCMP</display-name>
      <ejb-name>PersonneCMP</ejb-name>
      <local-home>com.dvp.heritage.ejb.PersonneCMPHome</local-home>
      <local>com.dvp.heritage.ejb.PersonneCMP</local>
      <ejb-class>com.dvp.heritage.ejb.PersonneCMPBean</ejb-class>
      <persistence-type>Container</persistence-type>
      <prim-key-class>java.lang.Integer</prim-key-class>
      <reentrant>False</reentrant>
      <abstract-schema-name>PersonneCMP</abstract-schema-name>
      <cmp-field>
          <field-name>id</field-name>
      </cmp-field>
      <cmp-field>
          <field-name>nom</field-name>
      </cmp-field>
      <cmp-field>
          <field-name>prenom</field-name>
      </cmp-field>
      <primkey-field>id</primkey-field>
    </entity>
    <entity>
      <display-name>SalarieCMP</display-name>
      <ejb-name>SalarieCMP</ejb-name>
      <local-home>com.dvp.heritage.ejb.SalarieCMPHome</local-home>
      <local>com.dvp.heritage.ejb.SalarieCMP</local>
      <ejb-class>com.dvp.heritage.ejb.SalarieCMPBean</ejb-class>
      <persistence-type>Container</persistence-type>
      <prim-key-class>java.lang.Integer</prim-key-class>
      <reentrant>False</reentrant>
      <abstract-schema-name>SalarieCMP</abstract-schema-name>
      <cmp-field>
          <field-name>id</field-name>
      </cmp-field>
      <cmp-field>
          <field-name>salaire</field-name>
      </cmp-field>
      <primkey-field>id</primkey-field>
    </entity>
  </enterprise-beans>
  <relationships>
    <ejb-relation>
      <ejb-relation-name>salarie-personne</ejb-relation-name>
      <ejb-relationship-role>
        <description>salarieCMP</description>
        <ejb-relationship-role-name>SalarieRelationshipRole</ejb-relationship-role-name>
        <multiplicity>One</multiplicity>
        <relationship-role-source>
          <description>salarieCMP</description>
          <ejb-name>SalarieCMP</ejb-name>
        </relationship-role-source>
        <cmr-field>
          <description>personneCMP</description>
          <cmr-field-name>personneCMP</cmr-field-name>
        </cmr-field>
      </ejb-relationship-role>
      <ejb-relationship-role>
        <description>personneCMP</description>
        <ejb-relationship-role-name>PersonneRelationshipRole</ejb-relationship-role-name>
        <multiplicity>One</multiplicity>
        <relationship-role-source>
          <description>personneCMP</description>
          <ejb-name>PersonneCMP</ejb-name>
        </relationship-role-source>
      </ejb-relationship-role>
    </ejb-relation>
  </relationships>
  <assembly-descriptor>
    <container-transaction>
      <method>
        <ejb-name>PersonneCMP</ejb-name>
        <method-name>*</method-name>
      </method>
      <trans-attribute>Required</trans-attribute>
    </container-transaction>
    <container-transaction>
      <method>
        <ejb-name>SalarieCMP</ejb-name>
        <method-name>*</method-name>
      </method>
      <trans-attribute>Required</trans-attribute>
    </container-transaction>
  </assembly-descriptor>
</ejb-jar>

6. Réalisation des objets clients

Pour simplifier l'utilisation du modèle objet serveur, une classe utilitaire a été ajoutée. Ce Design Pattern est appelé Wrapper (ou Adapter) (ou Business Delegate). Vous pouvez trouver plus d'informations sur l'article Design Patterns J2EE et Design Patterns UML. La méthode getFacade est intéressante, elle consiste à récupérer l'interface Home de l'EJB Session Facade. Après le premier appel, cette référence est conservée. Cette technique simplifiée permet d'éviter de nombreuses localisations sur le serveur. En plus élaboré et centralisé, vous la trouverez sous le nom de Service Locator

 
Sélectionnez

package com.dvp.heritage;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.rmi.PortableRemoteObject;
import com.dvp.heritage.ejb.PersonneFacade;
import com.dvp.heritage.ejb.PersonneFacadeHome;

public class PersonneUtil {
  private static PersonneFacadeHome personneFacadeHome;
  protected static PersonneFacade getFacade() throws Exception {
    try {
      //Si il n'y a pas de référence ou si la référence n'est plus valide
      return personneFacadeHome.create();
    }
    catch (Exception ex) {
      Context ctx = new InitialContext();
      Object ref = ctx.lookup("com/dvp/heritage/PersonneFacade");
      personneFacadeHome = (PersonneFacadeHome) PortableRemoteObject.narrow(ref,
          PersonneFacadeHome.class);
    }
    return personneFacadeHome.create();
  }

  public static com.dvp.heritage.Personne find(Integer id) throws Exception {
    return getFacade().find(id);
  }

  public static void create(Personne personne) throws Exception {
    getFacade().create(personne);
  }

  public static void delete(Integer id) throws Exception {
    getFacade().delete(id);
  }

  public static void update(Personne personne) throws Exception {
    getFacade().update(personne);
  }
}

7. Exemple d'utilisation

Nous pouvons enfin faire plusieurs tests pour valider le code précédent. Voici les différentes étapes :

  • Création de deux personnes : Toto (Personne) et Bob (Salarié)
  • Affichage des données en utilisant le mécanisme du polymorphisme de la méthode toString().
  • Modification des deux personnes sans tenir compte de leur type.
  • Affichage à nouveau.
  • Suppression des deux personnes
 
Sélectionnez

package com.dvp.heritage;

public class HeritageMain {
  public static void main(String argv[]) throws Exception {
    create();
    try {
      show();
      update();
      show();
    }
    finally {
      delete();
    }
  }
  public static void show() throws Exception {
    Personne p1 = PersonneUtil.find(new Integer(1));
    Personne p2 = PersonneUtil.find(new Integer(2));
    System.out.println("p1 = " + p1);
    System.out.println("p2 = " + p2);
  }
  public static void delete() throws Exception {
    PersonneUtil.delete(new Integer(2));
    PersonneUtil.delete(new Integer(1));
  }
  public static void update() throws Exception {
    Personne p1 = PersonneUtil.find(new Integer(1));
    Personne p2 = PersonneUtil.find(new Integer(2));
    p1.setPrenom(p1.getNom() + " modif");
    PersonneUtil.update(p1);
    p2.setPrenom(p2.getNom() + " modif");
    PersonneUtil.update(p2);
  }
  public static void create() throws Exception {
    Personne toto = new Personne(new Integer(1), "Toto", "Toto");
    Salarie bob = new Salarie(new Integer(2), "Bob", "Robert", new Double(12000));
    PersonneUtil.create(toto);
    PersonneUtil.create(bob);
  }
}

Voici le résultat de la console lors de l'éxécution du programme

 
Sélectionnez

p1 = Type=com.dvp.heritage.Personne id=1 nom=Toto prenom=Toto
p2 = Type=com.dvp.heritage.Salarie id=2 nom=Bob prenom=Robert salaire=12000.0
p1 = Type=com.dvp.heritage.Personne id=1 nom=Toto prenom=Toto modif
p2 = Type=com.dvp.heritage.Salarie id=2 nom=Bob prenom=Bob modif salaire=12000.0

Voici les données après création:

Image non disponible
Image non disponible

Et les données après modification:

Image non disponible
Image non disponible

7.1. Analyse des flux SQL

Nous allons étudier le flux SQL généré lors de l'éxécution du programme.
Création de Toto et de Bob

 
Sélectionnez

INSERT INTO PERSONNE (ID, NOM, PRENOM) VALUES (1, 'Toto', 'Toto')
Connection.commit
INSERT INTO PERSONNE (ID, NOM, PRENOM) VALUES (2, 'Bob', 'Robert')
INSERT INTO SALARIE (ID, SALAIRE) VALUES (2, 12000.0)
Connection.commit

La création fonctionne à merveille, le flux minimum a été généré. Bob nécéssite deux INSERT. Regardons maintenant les opérations de type find:

 
Sélectionnez

SELECT ID, NOM, PRENOM FROM PERSONNE WHERE ID=1
SELECT ID, SALAIRE FROM SALARIE WHERE ID=1
Connection.commit
SELECT ID, NOM, PRENOM FROM PERSONNE WHERE ID=2
SELECT ID, SALAIRE FROM SALARIE WHERE ID=2
Connection.commit

La consultation des données se fait toujours en interrogeant les deux tables, c'est normale pour Bob, c'est du travail supplémentaire pour Toto. Le flux n'est pas optimal ici. Un moyen de s'en sortir est d'ajouter une information de la classe réelle dans la table Personne.
Regardons maintenant les modifications:

 
Sélectionnez

SELECT ID, NOM, PRENOM FROM PERSONNE WHERE ID=1
UPDATE PERSONNE SET PRENOM='Toto modif' WHERE ID = 1
Connection.commit
SELECT ID, NOM, PRENOM FROM PERSONNE WHERE ID=2
SELECT ID, SALAIRE FROM SALARIE WHERE ID=2
UPDATE PERSONNE SET PRENOM='Bob modif' WHERE ID = 2
Connection.commit

Le serveur d'application commence par consulter les données de la base et modifie uniquement les attributs changés. La modification dans les deux cas est bien optimisée.
Etudions maintenant le dernier cas, la supression:

 
Sélectionnez

SELECT ID, SALAIRE FROM SALARIE WHERE ID=2
SELECT ID, NOM, PRENOM FROM PERSONNE WHERE ID=2
DELETE FROM SALARIE WHERE ID = 2
SELECT ID, SALAIRE FROM SALARIE WHERE ID=2
DELETE FROM PERSONNE WHERE ID = 2
Connection.commit
SELECT ID, NOM, PRENOM FROM PERSONNE WHERE ID=1
SELECT ID, SALAIRE FROM SALARIE WHERE ID=1
DELETE FROM PERSONNE WHERE ID = 1
Connection.commit

De nombreux select pourraient être évités ici, ils sont dus au findObject qui sont appelés lors de ce traitement.

8. Conclusion

Avec cette illustration, vous venez de voir qu'il est tout à fait possible d'utiliser un vrai mécanisme d'héritage avec vos projets J2EE utilisant des objets Entiy. Cette technique, comme les designs patterns J2EE, est cependant fastidieuse à implémenter. Ce travail sera rentabilisé par une simplification du développement côté client.
Les derniers paragraphes mettent en évidence quelques lacunes des flux SQL, ces dernières peuvent être améliorées par l'ajout d'une information de type dans les tables de la base de données.

Toutes vos remarques et vos commentaires seront les bienvenus pour améliorer cet article.

Vous pouvez trouvez l'ensemble des sources ici (Projet JBuilder, facilement transportable sur d'autres IDE).

Voir également
Patterns J2EE / EJB de Christophe LUDET
Design Paterns de Sébastien MERIC
Le Forum J2EE

Ce document est issu de http://www.developpez.com et reste la propriété exclusive de son auteur.
La copie, modification et/ou distribution par quelque moyen que ce soit est soumise à l'obtention préalable de l'autorisation de l'auteur.