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

Request

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

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

- Auteur : Request

- Parution : 25 août 2003

- Licence : Ce document est issu de https://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.

Sommaire

- Introduction

- I. Problématique

- I-A. Script SQL

- II. Modélisation

- III. Réalisation des objets sérialisés

- III-A. Personne

- III-B. Salarie

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

- IV-A. Interface Remote: PersonneFacade

- IV-B. Implémentation Bean: PersonneBean

- IV-C. Interface Home: PersonneHome

- IV-D. Descripteur de déploiement

- V. Réalisation des EJB Entity CMP

- V-A. Interface Locale : PersonneCMP

- V-B. Implémentation Bean: PersonneCMPBean

- V-C. Interface Home : PersonneCMPHome

- V-D. Interface Locale : SalarieCMP

- V-E. Implémentation Bean : SalarieCMPBean

- V-F. Interface Home : SalarieCMPHome

- V-G. Descripteur de déploiement

- VI. Réalisation des objets clients

- VII. Exemple d'utilisation

- VII-A. Analyse des flux SQL

- VIII. Conclusion

Introduction

La technologie de persistance objet 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 objet qu'une nécessité. Mais, comme avec l'ensemble des technologies J2EE, un bon design patterns nous rendra accessible cette fonctionnalité.

I - Problématique

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

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.

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) :

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êmes données dans les deux tables :

I-A - Script SQL

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);

II - Modélisation

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

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 retrouvons 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, quant à 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.

III - Réalisation des objets sérialisés

III-A - 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.

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;
}
}

III-B - 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.

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;
}
}

IV - 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é).

IV-A - Interface Remote: PersonneFacade

Rien de spécial pour cette classe.

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;
}

IV-B - Implémentation Bean: PersonneBean

Voici la classe principale du projet, la façade, 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.

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éseau 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 lecteurs ici ? ;o)

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) {
//peut 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) {
//Peut 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) {
//Peut 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) {
//Peut mieux faire...
throw new EJBException(ex);
}
}
}

IV-C - Interface Home: PersonneHome

Rien de spécial pour cette classe.

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;
}

IV-D - Descripteur de déploiement

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

<?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>

V - 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.

V-A - Interface Locale : PersonneCMP

Définition des attributs de Personne.

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();
}

V-B - Implémentation Bean: PersonneCMPBean

Rien de spécial pour cette implémentation.

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;
}
}

V-C - Interface Home : PersonneCMPHome

Rien de spécial pour cette implémentation.

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;
}

V-D - Interface Locale : SalarieCMP

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

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();
}

V-E - Implémentation Bean : SalarieCMPBean

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

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;
}
}

V-F - Interface Home : SalarieCMPHome

Rien de spécial pour cette implémentation.

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;
}

V-G - Descripteur de déploiement

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

<?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>

VI - 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 :

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 {
//s’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);
}
}

VII - 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.

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'exécution du programme

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 :

Et les données après modification :

VII-A - Analyse des flux SQL

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

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écessite deux INSERT. Regardons maintenant les opérations de type find :

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 normal 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 :

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.
Étudions maintenant le dernier cas, la suppression :

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.

VIII - 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 trouver l'ensemble des sources ici (Projet JBuilder, facilement transportable sur d'autres IDE).