Prenons comme exemple un modèle simple qui comporte des Commune et des Maire. Chaque commune possède un maire, et un maire ne peut être maire que d'une seule commune. Nous avons donc bien une relation 1:1 entre les communes et leurs maires. Supposons que dans notre exemple, ce sont les communes qui tiennent la relation entre les communes et les maires.

L'entité esclave doit préciser un champ retour par une annotation @OneToOne et un attribut mappedBy, qui doit référencer le champ qui porte la relation côté maître. Créons par exemple un champ retour dans notre classe Maire. L'attribut mappedBy est défini sur l'entité esclave de la relation.


Définir une relation bidirectionnelle en JPA permet de créer le jeu de clés étrangères qui permet de garantir l'intégrité référentielle de la base de données. Il appartient au code Java de fixer la valeur du champ retour afin de garantir la cohérence du modèle objet.

Une relation bidirectionnelle ne se comporte pas comme deux relations unidirectionnelles. Effectivement, une relation unidirectionnelle est caractérisée par une colonne de jointure sur la table maître, et une clé étrangère de cette colonne vers la clé primaire de l'entité en relation.

Deux relations unidirectionnelles créeront donc deux colonnes de jointure, dans chacune des deux tables en relation.

Une relation bidirectionnelle ne crée pas cette deuxième colonne de la table de jointure, de la table esclave vers la table maître. Lorsque l'on veut lire la relation retour, à partir de l'esclave, une requête est lancée sur la base, en utilisant le caractère bidirectionnel de la relation.


Une relation 1:p est caractérisée par un champ de type Collection dans la classe maître. La classe esclave ne porte pas de relation retour. Cette relation peut être spécifiée soit par l'annotation @OneToMany ou @ManyToMany.

Elle peut être unidirectionnelle ou bidirectionnelle. Dans ce second cas, le côté maître est obligatoirement le côté qui tient la relation monovaluée.

Dans ces deux cas, JPA crée une table de jointure entre les deux tables associées aux deux entités. Cette table de jointure porte une clé étrangère vers la clé primaire de la première table, et une clé étrangère vers la clé primaire de la deuxième table.

Voyons ceci sur un exemple.


Voici le schéma créé par EclipseLink.


On peut ici se poser une question : pourquoi la spécification JPA prévoit-elle la création d'une table de jointure alors que la relation est de type 1:p ? La réponse est simple : parce qu'aucune information n'existe sur le champ retour, et que JPA n'a donc aucune information sur la colonne de jointure à utiliser dans la table destination. Dans le cas 1:p bidirectionnel, nous verrons que JPA ne crée pas cette table de jointure.

Nous avons écrit que l'on pouvait utiliser l'annotation @OneToMany ou @ManyToMany pour spécifier cette relation. Cela dit, il faut noter que les deux annotations ne sont pas équivalente. Dans le premier cas, JPA ajoute une contrainte d'unicité sur la clé étrangère de la table de jointure vers la table Marin. C'est bien ce que nous voulons ici : un même marin ne peut pas appartenir à plusieurs équipages à la fois. Il faut toutefois bien avoir présent à l'esprit cette contrainte : une relation de type 1:p est parfois une relation @ManyToMany.

Une relation 1:p bidirectionnelle doit correspondre à une relation p:1 dans la classe destination de la relation. Comme pour le cas des relations 1:1, le caractère bidirectionnel d'une relation 1:p est marqué en définissant l'attribut mappedBy sur la relation.

JPA nous pose une contrainte ici : l'attribut mappedBy est défini pour l'annotation @OneToMany, mais pas pour l'annotation @ManyToOne. Or, comme nous l'avons vu dans le cas de l'annotation @OneToOne, mappedBy doit être précisé sur le côté esclave d'une relation. Dans le cas d'une relation 1:p bidirectionnelle, JPA ne nous laisse donc pas le choix de l'entité maître et de l'entité esclave.


Examinons le schéma de base généré par JPA.


JPA a créé une colonne de jointure dans la table de l'entité cible (ici Marin), de façon à établir la jointure. Il a de plus créé une clé étrangère vers la clé primaire de la table Bateau.

Comme on le voit, JPA n'a plus besoin dans ce cas d'une table de jointure pour coder cette relation. On retombe donc dans le cas nominal d'une relation 1:p avec colonne de jointure dans la table cible de la relation.

Une relation n:p est une relation multivaluée des deux côtés de la relation. La façon classique d'enregistrer ce modèle en base consiste à créer une table de jointure, et c'est ce que fait JPA.

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