4. Opération de persistance en façade

4.1. Introduction

Pour le moment notre façade ne fait pas grand chose de plus que dans l'exemple préliminaire de ce chapitre. Nous allons donc enrichir ses fonctionnalités, et exposer des fonctions de persistance pour notre modèle.

4.2. Enrichissement du service

On souhaite enrichir notre service de deux manières :
  • Faire en sorte que notre méthode createMarin crée bien un Marin, plutôt que de retourner -1 systématiquement (ça sera plus utile !).
  • Ajouter deux méthodes : findMarinById(long) et getAllMarins(), qui retournent respectivement le marin dont on a donné la clé primaire, et la liste de tous les marins en base.
Nous avons déjà écrit du code de création d'entités JPA dans le chapitre précédent. Ce code est juste un appel à la méthode entityMananger.persist(), dans une transaction que nous avons gérée manuellement. Dans un serveur d'applications, les choses ne se déroulent pas tout à fait de la même manière. Tout d'abord, comme nous l'avons entrevu sur notre unité de persistance, les transactions ne sont plus gérées par notre code, mais par le serveur directement. Dans son comportement par défaut, lorsque nous entrons dans une méthode d'un EJB (on appelle ces méthodes des business methods , ou méthode métier), le serveur d'application a ouvert une transaction pour nous. Lorsque l'on quitte cette méthode, le serveur tente de valider cette transaction, sans que nous n'ayons rien à faire. Si cette validation échoue, alors une exception est générée. De plus, l' entity manager que l'on utilise est fourni par le serveur d'applications, en tant que dépendance injectée. Ce mécanisme d'injection de dépendances nous permet de nous affranchir complètement de la création de nombre d'objets techniques. Voyons ces deux points sur l'exemple de notre EJB.

Exemple 62. Injection de l' entity manager dans un EJB

@Stateless(mappedName="MarinService")
 @Remote(MarinService.class)
 public  class MarinServiceImpl  implements MarinService {
    
     // création et injection d'un entity manager associé à  
     // l'unité de persistance nommée cours-ear-pu
     @PersistenceContext(unitName="cours-ear-pu")
     private EntityManager em ;

     @Override
     public  long createMarin(String nom) {

        Marin marin =  new Marin() ;
        marin.setNom(nom) ;
        em.persist(marin) ;

         return marin.getId() ;
    }
}

Cet exemple simple illustre ces deux concepts. Tout d'abord l'injection d'un entity manager : il suffit d'annoter une variable de type EntityManager avec @PersistenceContext. Cette annotation prend en attribut le nom de l'unité de persistance à laquelle cet entity manager est attaché. Ce nom doit donc être le même que celui que l'on a défini dans notre fichier persistence.xml. À chaque fois que notre méthode createMarin(String) sera appelée, le serveur d'applications créera un nouvel entity manager , que nous pourrons utiliser directement. On voit aussi que le code de gestion de la transaction a disparu : cette gestion est prise en charge par le serveur d'applications. En fait, appeler la méthode entityManager.getTransaction() jettera même une exception : lorsque nous sommes dans ce contexte, il n'est pas autorisé de tenter d'accéder à cette transaction. Ce mécanisme d'injection de dépendances est apparu en JEE5, et il est massivement utilisé en JEE6. Il peut paraître un peu choquant au premier abord : nulle part on n'initialise cette variable em dans notre code, ce n'est qu'au moment de son utilisation qu'elle est initialisée par le serveur d'applications.

4.3. Création de la méthode findMarinById(long)

Une fois ce squelette d'application en place, il est très facile d'ajouter des méthodes à notre EJB. La première que l'on se propose d'écrire retrouve un marin dont on passe la clé primaire en paramètre. Une fois cette méthode ajoutée à l'interface de MarinService, il nous faut créer son implémentation dans MarinServiceImpl.

Exemple 63. Ajout de la méthode findMarinById(long)

@Override
 public Marin findMarinById(long id) {

    Marin marin = em.find(Marin.class, id) ;
     return marin ;
}

Le principe d'écriture de cette méthode est le même que la précédente : on sait que l' entity manager est créé pour nous, donc on l'utilise de la même façon que dans les exemples du chapitre précédent. Notons que, suivant ce qui a été expliqué sur la gestion des transactions, cette requête crée un objet attaché à la transaction. C'est un peu dommage, puisqu'aucune modification n'est faite dessus. On paye ici un surcoût inutile, ce qui, dans une application d'entreprise n'est jamais une bonne idée. Nous verrons dans la suite comment il est possible de contrôler ce point dans un EJB.

4.4. Ajout de la méthode findAllMarins()

Pour cette dernière méthode, on peut ajouter une requête nommée à notre classe Marin.

Exemple 64. Création d'une requête nommée sur la classe Marin

@NamedQueries({
    @NamedQuery(
        name="Marin.findAll",
        query="select marin from Marin marin")
})
 @Entity
 public  class Marin  implements Serializable {

     // reste de la classe
}

Une fois cette requête nommée créée, on peut l'utiliser normalement. Écrivons la méthode findAllMarins() de notre EJB.

Exemple 65. Utilisation d'une requête nommée dans un EJB

@Override
 public List<Marin> findAllMarins() {

    Query query = em.createNamedQuery("Marin.findAll") ;

    List<Marin> marins = query.getResultList() ;
     return marins ;
}

4.5. Utilisation dans un code client

On peut donc maintenant enrichir le code de notre application cliente.

Exemple 66. Code de l'application cliente

public  static  void main(String... args)  throws NamingException {

     // connexion à l'annuaire de notre serveur d'application
     // ne pas oublier le fichier jndi.properties !
    InitialContext context =  new InitialContext() ;

     // requête sur l'annuaire : récupération d'une référence sur notre EJB
    MarinService marinService = (MarinService)context.lookup("MarinService") ;
    
     // utilisation de cet EJB
     long id = marinService.createMarin("Surcouf") ;
    System.out.println("Id = " + id) ;

    Marin marin = marinService.findMarinById(id) ;
    System.out.println("Marin = " + marin) ;

    List<Marin>marins = marinService.findAllMarins() ;
     for (Marin m : marins) {
        System.out.println("Marin = " + m) ;
    }
}

Notons que l'exécution de ce code client est indépendante de l'exécution de notre EJB. Cet EJB est en fonctionnement permanent, à la différence du code client, qui s'éteint une fois que la méthode main() a quitté. Or la base ne se réinitialise qu'au démarrage de notre unité de persistance, qui est maintenant attachée à l'application entreprise. Donc, si l'on lance notre client plusieurs fois de suite, on verra autant de marins "Surcouf" créés en base. Notons enfin, que Netbeans a une bonne capacité à rafraîchir en temps réel l'application entreprise déployée, que le simple fait de modifier la classe Marin, ou notre EJB, peut entraîner immédiatement le redéploiement de cette application.
JPA & EJB
Retour au blog Java le soir
Cours & Tutoriaux
Table des matières
Introduction
1. Objet du mapping objet / relationnel
2. Un peu d'histoire
Un premier exemple
1. Introduction
2. Création de l'environnement technique
2.1. Introduction
2.2. Création de la base Derby
2.3. Création du projet NetBeans et d'une première entité
2.4. Structure d'un projet persistant
2.5. Une première classe persistante
2.6. Un premier fichier persistence.xml
3. Utilisation de ce premier exemple
3.1. Écriture du code d'utilisation
3.2. Exécution de notre premier exemple
3.3. Modification de la class Marin
3.4. Opérations CRUD
Mettre un jeu de classes en base
1. Introduction
2. Définition d'une entité JPA
2.1. Écriture de l'entité
2.2. Annotation de l'entité
2.3. Annotations des champs
2.4. Exemple d'utilisation
3. Opérations sur les entités
3.1. Introduction
3.2. Opération PERSIST
3.3. Opération REMOVE
3.4. Opération REFRESH
3.5. Opération DETACH
3.6. Opération MERGE
4. Mise en relation d'entités
4.1. Introduction
4.2. Relations unidirectionnelles et bidirectionnelles
4.3. Relation 1:1
4.4. Relation 1:p
4.5. Relation p:1
4.6. Relation n:p
4.7. Comportement cascade
4.8. Effacement des entités orphelines
5. Charger des entités et leurs relations
6. Objets inclus
6.1. Introduction
6.2. Déclaration d'un objet inclus
6.3. Utilisation d'objets inclus
6.4. Cas où l'objet inclus est nul
6.5. Renommer les colonnes incluses
6.6. Collections d'objets inclus
L'API Collection en base
1. Introduction
2. Enregistrer une collection d'entités
2.1. Enregistrement d'une collection simple
2.2. Enregistrement d'un Set
2.3. Enregistrement d'une List
3. Enregistrer une collection de types de base
4. Enregistrement d'une Map
4.1. Table de hachage de type (type de base, entité)
4.2. Cas où la clé est un champ de la valeur
4.3. Cas d'une table (entité, entité)
Héritage
1. Introduction
2. Enregistrement d'une hiérarchie de classes
2.1. Entité et super-classe non enregistrée
2.2. Position du problème
2.3. Trois façons de faire
3. Stratégie SINGLE_TABLE
3.1. Fonctionnement
3.2. Mise en place
3.3. Limitations
4. Stratégie JOINED
4.1. Fonctionnement
4.2. Mise en place
4.3. Limitations
5. Stratégie TABLE_PER_CLASS
5.1. Fonctionnement
5.2. Mise en place
5.3. Limitations
Requêtes
1. Introduction
2. Un premier exemple
2.1. Écriture d'une première requête
2.2. Exécution d'une première requête
2.3. Exécution d'une première requête d'agrégation
3. Définition de requêtes
3.1. Requêtes dynamiques
3.2. Requêtes paramétrées
3.3. Requêtes nommées
3.4. Requêtes natives
4. Exécution, analyse du résultat
4.1. Exécution d'une requête dynamique
4.2. Exécution d'une requête nommée
4.3. Analyse du résultat
4.4. Cas des résultats de grande taille
4.5. Remarques
5. Clause From
5.1. Définition des entités
5.2. Jointures dans la clause From
5.3. Remarque finale sur les jointures en JPQL
6. Clause Where
6.1. Variables et chemins dans une clause where
6.2. Expressions conditionnelles et opérateurs
6.3. Requêtes imbriquées
6.4. Opérateurs any, all et some
6.5. Expressions fonctionnelles
7. Clauses Group By et Having
8. Opérations Update et Delete
EJB
1. Introduction
2. Un premier exemple
2.1. Introduction
2.2. Installation dans Glassfish à l'aide de Netbeans
2.3. Création d'un premier EJB
2.4. Déploiement de notre premier EJB
2.5. Création d'un client
3. Mise en oeuvre du pattern session facade
3.1. Introduction
3.2. Modèle objet
3.3. Définition de l'unité de persistance
3.4. Assemblage de notre application
3.5. Assemblage et déploiement
3.6. Utilisation du client
4. Opération de persistance en façade
4.1. Introduction
4.2. Enrichissement du service
4.3. Création de la méthode findMarinById(long)
4.4. Ajout de la méthode findAllMarins()
4.5. Utilisation dans un code client
5. Types d'EJB
5.1. Introduction
5.2. Qu'est-ce qu'un EJB ?
5.3. Écriture d'un EJB session
5.4. Qu'est-ce qu'une méthode métier ?
5.5. EJB avec ou sans état
5.6. Gestion des transactions
5.7. Restrictions
6. Cycle de vie d'un EJB
6.1. Cas des EJB sans état
6.2. Cas des EJB avec état
6.3. Injection de dépendances
7. Transaction gérée par l'EJB
7.1. Introduction
7.2. Déclaration du mode transactionnel
7.3. Gestion de la transaction
7.4. Fonctionnement de la transaction
7.5. Cas des EJB avec état
8. Transaction gérée par le serveur
8.1. Introduction
8.2. Déclaration du mode transactionnel
8.3. Gestion de la transaction
8.4. Fonctionnement de la transaction
8.5. Remarques
9. Intercepteurs
9.1. Introduction
9.2. Aperçu général
9.3. Cycle de vie d'un intercepteur
9.4. Object InvocationContext
9.5. Interception d'un EJB ou d'une méthode métier
9.6. Exemple de mise en œuvre d'un intercepteur