La première façon de créer une requête JPQL consiste à créer une chaîne de caractères qui porte cette requête, et à la passer à la méthode createQuery() de l' entity manager . Cette approche est parfaitement légale, mais elle pose un problème, et comporte un piège.

Le problème est que la traduction de la requête JPQL en SQL se fait au moment de la création de l'objet Query, et à chaque instanciation de cet objet. Si la requête est créée dans une servlet, ou dans un EJB, le coût de traitement devra être payé à chaque fois que l'utilisateur fait appel à cette servlet ou à cet EJB.

Le piège se trouve dans la façon dont on veut prendre en compte les paramètres de la requête. Supposons que l'on ait la méthode suivante.


Cette requête est censée retourner tous les marins dont le nom est passé en paramètre, et c'est ce qu'elle fait. Cela dit, on peut aussi lui passer en paramètre la chaîne de caractères suivante :

Surcouf' and prenom = 'Robert

Et dans ce cas, elle effectue une tout autre requête. Cette attaque, connue sous le nom d'injection de code est très classique, et doit être absolument évitée. Pour cela, JPQL expose une fonctionnalité analogue aux preparedStatement de JDBC.

Il existe deux moyens de paramétrer une requête. Les deux moyens consistent à placer des éléments particuliers dans la chaîne JPQL, puis de donner une valeur à ces éléments. Dans le premier cas ces éléments sont juste numérotés : ?1, ?2, etc... Dans le second cas, ils sont nommés : :nom, :adresse, etc...

Dans de nombreux cas, les requêtes utilisées sont connues dès la compilation de notre application. JPQL nous offre alors la possibilité de déclarer ces requêtes en tant que requêtes nommées . Ces requêtes nommées sont déclarées dans des annotations, et donc analysées par l'implémentation JPA au chargement de la classe. Elles peuvent donc être converties en code SQL au moment de ce chargement, et ne pas surcharger l'exécution de notre application.

Une requête nommée est déclarée dans une annotation @NamedQuery. Cette annotation prend deux attributs : name, qui porte le nom de la requête, et query, qui porte la requête JPQL. Le nom de la requête doit être unique pour une même unité de persistance. Sans que cela soit une obligation, il est donc pratique de le préfixer par le nom d'une entité.

Plusieurs annotations @NamedQuery peuvent être regroupées dans une annotation @NamedQueries, qui prend un tableau de @NamedQuery en attribut. Ces deux annotations doivent être posées sur la classe d'une entité JPA.


Remarquons plusieurs choses sur cet exemple.

Tout d'abord, nous avons donné un nom explicite à notre entité : Marin. Sans cela, il aurait fallu utiliser le nom complet de la classe Marin dans nos requêtes JPQL.

Les annotations @NamedQueries doivent être posées sur la classe d'une entité. Rien ne dit que ces requêtes doivent nécessairement retourner des instances ou collection d'instances de ces entités. Il est simplement pratique, lisible et habituel de mettre sur une classe les requêtes qui s'y rapportent. On notera d'ailleurs que la dernière de nos requête retournent un entier.

Enfin, et afin de garantir l'unicité du nommage de nos requêtes au niveau de l'unité de persistance, nous avons préfixé le nom de ces requêtes par le nom de l'entité sur laquelle elles sont définies. Il ne s'agit pas d'une obligation, mais d'une bonne façon de faire.

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