Considérons l'exemple suivant.


Nous avons volontairement laissé la déclaration List sans la typer, car son contenu n'est plus le même que dans le cas où notre requête portait sur une entité JPA.

Chaque élément de cette liste est un tableau. Il y a autant d'éléments tableau qu'il y a de lignes dans le résultat de notre requête SQL.

Examinons à présent le contenu de chacun de ces tableaux.

Pour cette requête, chaque ligne du résultat est composée de deux champs : nom et prenom, qui sont deux chaînes de caractères. On peut spécifier tous les champs que l'on veut, quel que soit leur type : type de base ou entité.

JPA construit un tableau pour chaque ligne du résultat. Chacun de ces tableaux est composé d'autant d'éléments qu'il y a de champs spécifiés. Chaque élément correspond à un des champs spécifiés, dans le bon ordre.

Nous pouvons donc écrire le code qui va nous permettre d'analyser ce résultat.


Cette analyse est un peu complexe techniquement, et surtout très fragile. L'utilisation de cast sur des éléments lus en aveugle sera effectivement pénible à mettre au point. De plus, le moindre changement de modèle risque de faire échouer tantôt la requête, tantôt son analyse, puisque l'on ne manipule que des Object, convertis à la volée.

Notons que ce système fonctionne également si l'un des champs spécifiés dans la requête est une entité JPA. Dans ce cas, cette entité se retrouvera dans la liste des éléments de la requête.

Il est enfin possible, en JPQL, de construire des objets résultat directement à partir des requêtes. Continuons avec l'exemple de nos objets marin, et de notre requête qui retourne leurs noms et prénoms. Construisons une classe simple, un bean, capable de stocker chaque ligne du résultat de notre requête.


Cet objet modélise exactement chaque ligne du résultat de notre requête précédente. On peut écrire à nouveau cette requête, de la façon suivante.


La nouveauté de cette requête est l'appel à la construction de l'objet NameBean, en passant au constructeur les paramètres du résultat de la requête. Cet objet résultat doit être désigné par son nom complet (donc avec le nom du package), et, bien sûr, ce constructeur doit exister. Notons que de plus, il ne doit pas y avoir ambiguïté sur ce construteur.

Écrivons le code qui permet d'analyser cette requête.


Cette approche fonctionne également si l'un des champs sélectionnés est une entité JPA.

L'interaction entre les requêtes et les transactions est un point qui mérite que l'on s'y attarde.

Si un résultat de requête va être exploité en vue de modifier les objets retournés par cette requête, alors cette requête doit être exécutée dans une transaction. Ces objets seront créés dans le contexte de cette transaction, et pourront donc être modifiés ou effacés sans problème.

En revanche, si un résultat de requête ne sera exploité qu'en lecture, par exemple à des fins d'affichage, alors cet attachement des objets résultat à la transaction est une surcharge inutile. Exécuter la requête à l'extérieur de la transaction sera moins coûteux en temps de calcul.

Enfin, que se passe-t-il lorsque l'on modifie des objets, et que dans la même transaction on exécute une requête JPQL dont le résultat comporte ces objets modifiés ?

Pour répondre à cette question, il faut bien distinguer les appels à la méthode find() de l' entity manager , et les requêtes telles que nous les avons vues dans cette partie.

La méthode em.find() demande un objet persistant par sa clé primaire. Si l' entity manager possède cet objet dans son cache, alors il le retourne sans faire de requête sur la base. Comme on ne peut pas changer la clé primaire d'un objet, tout se passera bien.

Les requêtes JPQL fonctionnent différemment : elles sont converties en SQL, puis exécutées sur la base de données. On peut être dans une configuration où un objet se trouve toujours en base dans l'état dans lequel il était avant la transaction, et dans le cache de l' entity manager , dans un état modifié. La modification de cet état peut faire que sa version en base vérifie les contraintes de la requête, mais pas sa version dans le cache. Pour éviter ce problème, une façon de faire consiste à recopier le cache de l' entity manager en base, ce sont cette fois les bons objets qui seront retournés par la requête. Notons que certaines implémentations peuvent aussi avoir des moyens pour exécuter des requêtes SQL à la fois sur le cache de chaque entity manager et la base de données. Dans ce cas il n'y a pas à recopier le cache en base avant l'exécution de la requête.

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