6. Clause Where

Nous avons déjà écrit plusieurs clauses where dans nos exemples, formalisons tout ceci ici.

6.1. Variables et chemins dans une clause where

Une clause where utilise le plus souvent des variables. Toutes ces variables doivent être définies dans la clause from. On peut définir un chemin à partir d'une variable, qui permet de naviguer dans le modèle objet d'une entité. Ainsi, si une requête définit une variable bateau, alors bateau.equipage désigne le champ equipage de la classe bateau. On ne peut utiliser une relation multivaluée dans une clause where que dans deux cas :
  • pour tester si la relation associée est vide ( is empty) ;
  • pour tester le cardinal de la relation associée ( size()).
Tous les autres cas d'utilisation d'une relation multivaluée dans la clause select sont illégaux en JPQL.

6.2. Expressions conditionnelles et opérateurs

JPQL supporte les expressions booléennes suivantes : and, or, not. Les opérateurs suivants sont supportés par JPQL.
  • L'opérateur de navigation ' .'.
  • Les opérateurs arithmétiques : le + et le - unaires, les opérateurs de multiplication ( *), division ( /), addition ( +) et soustraction ( -).
  • Les opérateurs de comparaison : =, >, >=, <, <=, <>, [NOT] BETWEEN, [NOT] LIKE, [NOT] IN, IS [NOT] NULL, IS [NOT] EMPTY, [NOT] MEMBER [OF], [NOT] EXISTS.
  • Les opérateurs logiques : NOT, AND et OR.
Examinons précisément les opérateurs de comparaison, dont le fonctionnement et la syntaxe peuvent être parfois un peu subtils.

6.2.1. Opérateur Between

Cet opérateur permet de tester si l'argument se trouve entre deux valeurs. Les types supportés sont les nombres, les chaînes de caractères et les dates. On peut toujours se passer de cet opérateur, et utiliser deux expressions reliées par un AND à la place.

6.2.2. Opérateur In

Cet opérateur permet de tester si la valeur en paramètre se trouve dans une collection. Cette collection peut être une relation multivaluée, le résultat d'une requête imbriquée, ou une collection explicitement écrite.

Exemple 51. Utilisation de l'opérateur In

select marin 
from Marin marin
where marin.nom in ('Surcouf',  'Tabarly')

select marin 
from Marin marin 
where marin.commune in (select commune 
                        from Commune commune join commune.maire maire
                        where maire.nom = :nom)

La première requête se comprend assez simplement. La deuxième sélectionne tous les marins dont la commune de naissance a le maire que l'on passe en paramètre. Notons ici que l'on aurait pu écrire cette requête à l'aide d'une jointure, ce qui est souvent le cas avec les requêtes SQL IN. Notons que l'utilisation de ces requêtes est souvent moins performante que l'écriture de jointures.

6.2.3. Opérateur Like

Cet opérateur permet de comparer des chaînes de caractères à l'aide d'expressions régulières très simples. Ces expressions régulières supportent deux caractères spéciaux : l' underscore ( _) qui représente un unique caractère quelconque, et le pourcent ( %) qui représente toute séquence de caractères quelconques, y compris la séquence vide.

6.2.4. Opérateur Is Null

Cet opérateur permet de tester si une valeur est nulle ou pas.

6.2.5. Opérateur Is Empty

Cet opérateur prend en paramètre une collection, qui peut être une relation multivaluée, ou une requêtre imbriquée. Il retourne true si cette collection est vide.

6.2.6. Opérateur Member Of

Cet opérateur permet de tester si un élément fait partie d'une collection ou pas. Cette collection ne peut être qu'une relation multivaluée.

6.2.7. Opérateur Exists

Cet opérateur permet de tester si une requête imbriquée retourne au moins une valeur. Si cette requête ne retourne aucune valeur, alors l'évaluation est false.

6.3. Requêtes imbriquées

JPQL supporte les requêtes imbriquées dans les clauses where et having. Voyons un exemple de sous-requête.

Exemple 52. Requête imbriquée

select bateau
from Bateau bateau
where (select count(*)
       from bateau.equipage) >  10 

Dans ce cas on ne sélectionne que les bateaux qui ont plus de 10 membres d'équipage. Notons que l'on aurait pu écrire la même requête sans faire de requête imbriquée, ce qui est souvent le cas.

6.4. Opérateurs any, all et some

Ces trois opérateurs s'utilisent conjointement avec les requêtes imbriquées. Ils permettent de comparer un élément donné avec l'ensemble des éléments de la collection retournée par la requête devant laquelle ils se placent. Voyons ceci sur un exemple d'utilisation de l'opérateur some. Les deux autres opérateurs s'utilisent exactement suivant la même syntaxe. L'opérateur all signifie que la comparaison doit être vraie pour tous les éléments de la liste. L'opérateur some signifie qu'elle est vraie pour une partie de ces éléments. Elle est fausse si le résultat de la sous-requête est vide. L'opérateur any est synonyme de l'opérateur some.

Exemple 53. Opérateur some

select capitaine
from Bateau bateau join bateau.capitaine capitaine 
where bateau.capitaine = capitaine and 
      bateau.equipage is not empty and 
      capitaine.salaire < some (select marin.salaire from bateau.equipage marin)

La requête précédente sélectionne les capitaines dont le salaire est inférieur à au moins un des salaires des marins de l'équipage du bateau qu'ils commandent. On remarquera plusieurs choses sur cette requête :
  • L'entité bateau tient la relation unidirectionnelle avec capitaine. Donc la jointure doit s'exprimer sous la forme Bateau bateau join bateau.capitaine.
  • On ne sélectionne que les bateaux dont l'équipage n'est pas vide par bateau.equipage is not empty.
  • On utilise dans la requête imbriquée, le bateau courant défini dans la requête principale.
  • L'écriture bateau.equipage marin permet de désigner un élément générique de la collection bateau.equipage. Il n'est pas utilisé ici dans la clause where, mais cela serait parfaitement possible.

6.5. Expressions fonctionnelles

6.5.1. Fonctions arithmétiques

JPQL offre toutes les fonctions arithmétiques classiques. Il supporte en plus les fonctions suivantes.
  • ABS(number) : retourne la valeur absolue du nombre passé en paramètre.
  • SQRT(number) : retourne la racine carrée du nombre passé en paramètre.
  • MOD(integer, integer) : retourne le reste de la division entière du premier entier passé en paramètre par le second.
  • SIZE(collection) : retourne le cardinal de la collection passée en paramètre. La collection doit être une relation multivaluée.
  • INDEX(collection) : retourne la position de l'objet courant dans la collection passée en paramètre. Cette collection doit être un alias dans une requête JPQL.

6.5.2. Fonctions opérant sur les chaînes de caractères

Il offre également les fonctions suivantes, qui opèrent sur les chaînes de caractères :
  • CONCAT(string, string, ... : réalise la concaténation des chaînes de caractères passées en paramètres.
  • SUBSTRING(string, begin, length : extrait une sous-chaîne d'une chaîne donnée. Le paramètre begin désigne le premier caractère à sélectionner, et length le nombre de ces caractères. S'il n'est pas présent, alors la sous-chaîne s'arrête à la fin de string.
  • TRIM(string) : supprime les blancs en fin de chaîne de caractères. Cette fonction peut prendre deux arguments supplémentaires. Le premier peut prendre la valeur leading, trailing ou both. Il indique quel côté de la chaîne doit voir ses espaces supprimés, éventuellement les deux. Le second indique le caractère qui doit être supprimé, plutôt que les espaces. Exemple : TRIM(LEADING 'a' FROM nom) retourne une chaîne dont on a supprimé les a en début.
  • LOWER(string) et UPPER(string) : retournent une chaîne de caractères résultat de la mise en minuscule ou en majuscule (respectivement) de la chaîne passée en paramètres.
  • LENGTH(string) : retourne le nombre de caractères dans la chaîne passée en paramètres.
  • LOCATE(toLocate, inString, index) : retourne la position de la chaîne toLocate dans la chaîne inString. La recherche commence à l'index index, qui est un argument optionnel.

6.5.3. Fonctions opérant sur les dates

Trois fonctions sont disponibles sur les dates : CURRENT_DATE, CURRENT_TIME et CURRENT_TIME_STAMP. Ces trois fonctions retournent la date courante du serveur, dans chacun des trois formats.

6.5.4. Fonction opérant sur une entité

Il existe enfin une fonction qui permet de tester le type réel d'une entité : TYPE. Cette fonction s'utilise de la façon suivante.

Exemple 54. Fonction TYPE

select personne  
from Personne personne
where TYPE(personne) IN (Marin, Capitaine)

Dans notre exemple, on sélectionne toutes les personnes qui sont marin ou capitaine, à l'exclusion des cuisiniers et des mousses.
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