Participer au site avec un Tip
Rechercher
 

Améliorations / Corrections

Vous avez des améliorations (ou des corrections) à proposer pour ce document : je vous remerçie par avance de m'en faire part, cela m'aide à améliorer le site.

Emplacement :

Description des améliorations :

Mapping d'une relation @OneToMany

Mapping d'une relation @ManyToOne Mapping d'une relation @ManyToMany



Accès rapide :
La vidéo
Introduction à la relation d'association de type « One-To-Many »
Gestion des collections avec JPA
Mapping d'une relation @OneToMany sans table d'association.
Implémentation, en base de données.
Implémentation du mapping JPA.
Test de la relation d'association.
Mapping d'une relation @OneToMany avec table d'association.
Implémentation, en base de données.
Implémentation du mapping JPA.
Test de la relation d'association.

La vidéo

Cette vidéo vous présente les différentes techniques de mapping des relations de type @OneToMany (avec ou sans table de jointures) proposées par JPA.


Mapping JPA d'une relation d'association de type @OneToMany

Introduction à la relation d'association de type « One-To-Many »

Nous allons voir, au travers de ce document, comment réaliser des associations de type « One-To-Many » avec JPA et Hibernate. Une relation de type « One-To-Many » a du sens quand un élément est associé à plusieurs autres éléments : par exemple, un utilisateur de notre site de vente en ligne peut avoir passé plusieurs commandes. Si une telle relation est mise en oeuvre, il vous sera possible de retrouver très simplement toutes les commandes d'un utilisateur.

Le diagramme UML ci-dessous vous montre l'association que nous allons chercher à mettre en oeuvre. La classe Command représentera la commande et la classe User représentera la personne ayant passé une ou plusieurs commandes. La relation « One-To-Many » est portée par l'utilisateur (regardez bien le sens de la flèche).

Diagramme UML d'une relation « One-To-Many »

Mais attention : en base de données, vous avez deux manières de représenter une relation de type « One-To-Many ».

le terme de table d'association est aussi parfois appelé table de jointure. Il s'agit d'une table en base de données permettant d'associer deux enregistrements situés dans deux autres tables de la base de données en utilisant des « foreign keys ».

Vous aurez certainement constaté que l'exemple proposé est très proche de celui vu dans le chapitre précédent. En fait tout est histoire de point de vue : quel est le sens de la relation que l'on considère. Dans le chapitre précédent nous avions vu comment retrouver l'utilisateur associé à une commande (il s'agissait d'une relation de type « Many-To-One »). Nous regardons maintenant comment récupérer toutes les commandes d'un utilisateur (le sens de la relation est donc inversé, soit une relation de type « One-To-Many »).

De plus, vous pouvez considérer une relation bidirectionnelle : dans ce cas, on peut naviguer d'un côté de la relation à l'autre et inversement. Etant donné que nous avons déjà travaillé un sens de la relation, je vous propose, dans ce chapitre, de compléter nos exemples avec l'autre facette du problème. Voici un diagramme UML montrant la relation de navigation bidirectionnelle.

Diagramme UML d'une relation bidirectionnelle

Gestion des collections avec JPA

Une relation de type « One-To-Many » implique que nous allons récupérer des ensembles d'objets (to many). Il faut savoir que vous ne pouvez pas faire n'importe quoi à ce niveau. La règle la plus importante à garder à l'esprit et que vous devez obligatoirement typer les collections d'un mapping JPA par interfaces : si vous utiliser les classes d'implémentations (java.util.ArrayList, java.util.Vector...) vous produirez inexorablement une erreur d'exécution lors du chargement en mémoire des données du mapping.

Voici, pour information, un exemple d'erreur produite : j'ai typé une collection via la classe java.util.HashSet au lieu d'utiliser l'interface java.util.Set.

[main] WARN  | org.hibernate.orm.connections.pooling                        | HHH10001002: Using Hibernate built-in connection pool (not for production use!) (DriverManagerConnectionProviderImpl.java:70)
Exception in thread "main" org.hibernate.AnnotationException: Illegal attempt to map a non collection as a @OneToMany, @ManyToMany or @CollectionOfElements: fr.koor.webstore.business.User.commands
    at org.hibernate.cfg.annotations.CollectionBinder.getCollectionBinder(CollectionBinder.java:324)
    at org.hibernate.cfg.AnnotationBinder.processElementAnnotations(AnnotationBinder.java:1899)
    at org.hibernate.cfg.AnnotationBinder.processIdPropertiesIfNotAlready(AnnotationBinder.java:913)
    at org.hibernate.cfg.AnnotationBinder.bindClass(AnnotationBinder.java:740)
    at org.hibernate.boot.model.source.internal.annotations.AnnotationMetadataSourceProcessorImpl.processEntityHierarchies(AnnotationMetadataSourceProcessorImpl.java:249)
    at org.hibernate.boot.model.process.spi.MetadataBuildingProcess$1.processEntityHierarchies(MetadataBuildingProcess.java:222)
    at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:265)
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:846)
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:873)
    at org.hibernate.jpa.HibernatePersistenceProvider.createEntityManagerFactory(HibernatePersistenceProvider.java:58)
    at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:55)
    at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:39)
    at fr.koor.webstore.Console.main(Console.java:16)

Mettons maintenant en oeuvre les deux possibilités de réalisation (avec ou sans table de jointure) d'une relation de type « One-to-Many ».

Mapping d'une relation @OneToMany sans table d'association.

Implémentation, en base de données.

dans ce chapitre, nous continuons à travailler en mode console (hors WAR Java/Jakarte EE) avec le SGBDr (Système de Gestion de Bases de données Relationnelles) MariaDB, comme proposé dans les exemples précédents. Je vous renvoie vers les tutoriels précédents pour de plus amples informations à ce sujet.

Voici le code SQL permettant de créer une base de données (pour le SGBDr MariaDB) avec deux tables mises en association par une clé de jointure.

-- ------------------------------------------------------------------------------
-- - Reconstruction de la base de données                                     ---
-- ------------------------------------------------------------------------------

DROP DATABASE IF EXISTS OneToMany;
CREATE DATABASE OneToMany;
USE OneToMany;

-- -----------------------------------------------------------------------------
-- - Construction de la table des utilisateurs                               ---
-- -----------------------------------------------------------------------------

CREATE TABLE T_Users (
    idUser              int         PRIMARY KEY AUTO_INCREMENT,
    login               varchar(20) NOT NULL,
    password            varchar(20) NOT NULL,
    connectionNumber    int         NOT NULL DEFAULT 0
);

INSERT INTO T_Users (login, password) 
VALUES ( 'Anderson',    'Neo' ),
       ( 'Skywalker',   'Luke' ),
       ( 'Plissken',    'Snake' ),
       ( 'Ripley',      'Ellen' ),
       ( 'Bond',        'James' );

SELECT * FROM T_Users;

-- -----------------------------------------------------------------------------
-- - Construction de la table des commandes                                 ---
-- -----------------------------------------------------------------------------

CREATE TABLE T_Commands (
    idCommand       int         PRIMARY KEY AUTO_INCREMENT,
    idUser          int         NOT NULL REFERENCES T_Users(idUser),
    commandDate     datetime    NOT NULL DEFAULT CURRENT_TIMESTAMP
);

INSERT INTO T_Commands (idUser)
VALUES (1), (2), (1);

SELECT * FROM T_Commands;
Le fichier Database.sql
nous avons donc produit une base de données contenant cinq utilisateurs et trois commandes. Deux commandes sont associées à l'utilisateur Anderson et une autre est associée à l'utilisateur Skywalker.

Pour créer votre base de données, connectez-vous en mode ligne de commande à votre serveur puis exécutez l'ordre suivant :

MariaDB [(none)]> source Database.sql
Query OK, 2 rows affected (0.066 sec)

Query OK, 1 row affected (0.000 sec)

Database changed
Query OK, 0 rows affected (0.010 sec)

Query OK, 5 rows affected (0.002 sec)
Records: 5  Duplicates: 0  Warnings: 0

+--------+-----------+----------+------------------+
| idUser | login     | password | connectionNumber |
+--------+-----------+----------+------------------+
|      1 | Anderson  | Neo      |                0 |
|      2 | Skywalker | Luke     |                0 |
|      3 | Plissken  | Snake    |                0 |
|      4 | Ripley    | Ellen    |                0 |
|      5 | Bond      | James    |                0 |
+--------+-----------+----------+------------------+
5 rows in set (0.000 sec)

Query OK, 0 rows affected (0.013 sec)

Query OK, 3 rows affected (0.002 sec)
Records: 3  Duplicates: 0  Warnings: 0

+-----------+--------+---------------------+
| idCommand | idUser | commandDate         |
+-----------+--------+---------------------+
|         1 |      1 | 2020-01-21 11:42:01 |
|         2 |      2 | 2020-01-21 11:42:01 |
|         3 |      1 | 2020-01-21 11:42:01 |
+-----------+--------+---------------------+
3 rows in set (0.000 sec)

MariaDB [OneToMany]>
vous l'aurez remarqué, rien ne change par rapport au chapitre précédent : vous auriez pu garder la même base de données.

Implémentation du mapping JPA.

Pour ajouter une relation de type « One-To-Many » à notre classe User veuillez procéder ainsi.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
@Entity @Table(name = "T_Users")
public class User {

    // Autres attributs

    @OneToMany( targetEntity=Command.class, mappedBy="user" )
    private List<Command> commands = new ArrayList<>();
            
    // Suite de la classe

}
Définition d'une relation de type @OneToMany

En base de données, la relation est implémentée en sens inverse : c'est la table T_Commands qui établie une relation sur la table T_Users. C'est ce qui est dit avec les attributs de l'annotation @OneToMany. L'attribut targetEntity identifie la classe Command à partir de laquelle on retrouvera le nom de la table en base de données. L'attribut mappedBy identifie l'attribut de classe user sur cette classe Command : en analysant le mapping JPA, Hibernate retrouvera le nom de la colonne, à savoir idUser, qui correspond à la clé de jointure avec la table T_Users.

dans l'exemple proposé ci-dessus, la collection représentant les commandes est bien définie par interface (java.util.List) : ce point-là est non négociable. Par contre, pour l'instanciation, on utilise bien une classe concrète.

Voici le code complet de la classe User. Notez aussi l'ajout de la méthode getCommands qui définie une propriété « read-only » sur l'ensemble des commandes. N'oubliez pas que Hibernate passe directement par les attributs pour construire vos instances, étant donné notre choix d'avoir annoté les attributs.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
 22 
 23 
 24 
 25 
 26 
 27 
 28 
 29 
 30 
 31 
 32 
 33 
 34 
 35 
 36 
 37 
 38 
 39 
 40 
 41 
 42 
 43 
 44 
 45 
 46 
 47 
 48 
 49 
 50 
 51 
 52 
 53 
 54 
 55 
 56 
 57 
 58 
 59 
 60 
 61 
 62 
 63 
 64 
 65 
 66 
 67 
 68 
 69 
 70 
 71 
 72 
 73 
 74 
 75 
package fr.koor.webstore.business;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity @Table(name = "T_Users")
public class User {

    @Id  @GeneratedValue( strategy=GenerationType.IDENTITY )
    private int idUser;
    
    private String login;
    
    private String password;
    
    private int connectionNumber;

    @OneToMany( targetEntity=Command.class, mappedBy="user" )
    private List<Command> commands = new ArrayList<>();

    
    public User() { }
    
    public User( String login, String password, int connectionNumber ) {
        super();
        this.setLogin( login );
        this.setPassword( password );
        this.setConnectionNumber( connectionNumber );
    }

    
    public int getIdUser() {
        return idUser;
    }
    
    public String getLogin() {
        return login;
    }
    
    public void setLogin(String login) {
        this.login = login;
    }

    public String getPassword() {
        return password;
    }
    
    public void setPassword(String password) {
        this.password = password;
    }
    
    public int getConnectionNumber() {
        return connectionNumber;
    }
    
    public void setConnectionNumber(int connectionNumber) {
        this.connectionNumber = connectionNumber;
    }
    
    public List<Command> getCommands() {
        return commands;
    }
    
    public String toString() {
        return this.idUser + ": " + this.login + "/" + this.password 
             + " - " + this.connectionNumber + " connexion(s)";
    }
    
}
La classe User et son mapping JPA
comme les attributs login, password et connectionNumber ont les mêmes noms que leurs colonnes associées en base de données, il n'est pas nécessaire de les marquer via des annotations JPA.

Pour ce qui est de la classe Command, rien ne change par rapport au chapitre précédent. Voici pour rappel, le code complet de la classe.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
 22 
 23 
 24 
 25 
 26 
 27 
 28 
 29 
 30 
 31 
 32 
 33 
 34 
 35 
 36 
 37 
 38 
 39 
 40 
 41 
 42 
 43 
 44 
 45 
 46 
 47 
 48 
 49 
 50 
 51 
 52 
 53 
 54 
 55 
 56 
 57 
 58 
 59 
package fr.koor.webstore.business;

import java.util.Date;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

@Entity  @Table(name="T_Commands")
public class Command {

    @Id @GeneratedValue(strategy=GenerationType.IDENTITY)
    private int idCommand;
    
    @ManyToOne @JoinColumn(name="idUser", nullable=false)
    private User user;
    
    private Date commandDate;
    
    
    public Command() {}
    
    public Command( User user, Date commandDate ) {
        this.setUser( user );
        this.setCommandDate( commandDate );
    }

    public int getIdCommand() {
        return idCommand;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }
    
    public Date getCommandDate() {
        return commandDate;
    }
    
    public void setCommandDate(Date commandDate) {
        this.commandDate = commandDate;
    }
    
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append( "Commande de >> " ).append( this.user )
               .append( " - " ).append( this.commandDate ).append( "\n" );
        return builder.toString();
    }   
    
}
La classe Command et son mapping JPA

Test de la relation d'association.

Pour configurer le projet JPA, il est nécessaire de fournir le fichier META-INF/persistence.xml. N'oubliez pas qu'il doit être accessible à partir de CLASSPATH. En voici sa définition.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
 22 
 23 
 24 
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence 
                   http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
             version="2.0">
             
    <persistence-unit name="WebStore">
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        
        <class>fr.koor.webstore.business.User</class> 
        <class>fr.koor.webstore.business.Command</class> 
        
        <properties>
            <property name="javax.persistence.jdbc.driver" value="org.mariadb.jdbc.Driver" />
            <property name="javax.persistence.jdbc.url" value="jdbc:mariadb://localhost/OneToMany" />
            <property name="javax.persistence.jdbc.user" value="root" />
            <property name="javax.persistence.jdbc.password" value="" />
            
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
            <property name="hibernate.format_sql" value="false" />
        </properties>
    </persistence-unit>
    
</persistence>
Le fichier de configuration JPA/Hibernate

Il vous faut, bien entendu un fichier de configuration pour Log4J (du moins, si vous souhaitez l'utiliser) : je vous renvoie à ce sujet sur un des chapitres précédents pour de plus amples informations.

Nous allons commencer par charger une instance d'utilisateur par sa clé primaire en invoquant la méthode entityManager.find. Puis nous allons chercher à afficher l'ensemble des commandes associées à cet utilisateur en utilisant notre relation de type « One-To-Many ». Voici l'exemple de code associé à ce scénario.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
 22 
 23 
 24 
 25 
 26 
 27 
 28 
 29 
 30 
 31 
package fr.koor.webstore;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

import fr.koor.webstore.business.Command;
import fr.koor.webstore.business.User;

public class Console {

    public static void main(String[] args) throws Exception {
        EntityManagerFactory entityManagerFactory = null;
        EntityManager entityManager = null;
        
        try {
        
            entityManagerFactory = Persistence.createEntityManagerFactory("WebStore");
            entityManager = entityManagerFactory.createEntityManager();
        
            User user = entityManager.find( User.class, 1 );            
            for( Command cmd : user.getCommands() ) {
                System.out.println( cmd );
            }
            
        } finally {
            if ( entityManager != null ) entityManager.close();
            if ( entityManagerFactory != null ) entityManagerFactory.close();
        }
    }
}
Exemple de chargement d'un utilisateur et de ses commandes.

Voici les résultats produits par cet exemple : notez qu'il y a bien deux commandes associées à cet utilisateur.

[main] WARN  | org.hibernate.orm.connections.pooling                        | HHH10001002: Using Hibernate built-in connection pool (not for production use!) (DriverManagerConnectionProviderImpl.java:70)
Commande de >> 1: Anderson/Neo - 0 connexion(s) - 2020-01-21 19:22:53.0

Commande de >> 1: Anderson/Neo - 0 connexion(s) - 2020-01-21 19:22:53.0

Mapping d'une relation @OneToMany avec table d'association.

Pour rappel, le terme de table d'association est aussi parfois appelé table de jointure. Il s'agit d'une table en base de données permettant d'associer deux enregistrements situés dans deux autres tables de la base de données en utilisant des « foreign keys » (des clés étrangères en français).

Parfois, des tables en base de données sont déjà existantes, mais aucune relation ne les relie. Vous pourriez simplement ajouter une clé de référence dans l'une des deux tables pour établir la relation. Mais si d'autres applications utilisent déjà ces tables, l'ajout d'une nouvelle colonne peut poser soucis. Dans ce cas, l'ajout d'une table d'association vous permet de gérer votre problème d'association sans impacter les applications existantes. Le contre coup étant qu'il faut réaliser plus de jointures pour extraire vos données (c'est un peu moins performant).

Implémentation, en base de données.

Voici le code SQL permettant de créer une base de données (pour le SGBDr MariaDB) avec nos deux tables T_Users et T_Commands ainsi qu'une nouvelle table d'association, appelée T_Commands_Users_Associations permettant de réaliser notre relation de type « Many-To-One ».

-- ------------------------------------------------------------------------------
-- - Reconstruction de la base de données                                     ---
-- ------------------------------------------------------------------------------

DROP DATABASE IF EXISTS OneToMany;
CREATE DATABASE OneToMany;
USE OneToMany;

-- -----------------------------------------------------------------------------
-- - Construction de la table des utilisateurs                               ---
-- -----------------------------------------------------------------------------

CREATE TABLE T_Users (
    idUser              int         PRIMARY KEY AUTO_INCREMENT,
    login               varchar(20) NOT NULL,
    password            varchar(20) NOT NULL,
    connectionNumber    int         NOT NULL DEFAULT 0
);

INSERT INTO T_Users (login, password) 
VALUES ( 'Anderson',    'Neo' ),
       ( 'Skywalker',   'Luke' ),
       ( 'Plissken',    'Snake' ),
       ( 'Ripley',      'Ellen' ),
       ( 'Bond',        'James' );

SELECT * FROM T_Users;

-- -----------------------------------------------------------------------------
-- - Construction de la table des commandes                                  ---
-- -----------------------------------------------------------------------------

CREATE TABLE T_Commands (
    idCommand       int         PRIMARY KEY AUTO_INCREMENT,
    commandDate     datetime    NOT NULL DEFAULT CURRENT_TIMESTAMP
);

INSERT INTO T_Commands () VALUES (), (), ();

SELECT * FROM T_Commands;

-- -----------------------------------------------------------------------------
-- - Construction de la table d'association T_Commands/T_Users               ---
-- -----------------------------------------------------------------------------

CREATE TABLE T_Commands_Users_Associations (
    idCommand   int   NOT NULL UNIQUE REFERENCES T_Commands(idCommand),
    idUser      int   NOT NULL REFERENCES T_Users(idUser)
);

INSERT INTO T_Commands_Users_Associations VALUES (1, 1), (2, 2), (3, 1);

SELECT * FROM T_Commands_Users_Associations;
Fichier Database.sql

Pour construire la base de données, veuillez de nouveau exécuter l'ordre source Database.sql dans la console de MariaDB. Voici les résultats produits.

MariaDB [(none)]> source Database.sql
Query OK, 3 rows affected (0.075 sec)

Query OK, 1 row affected (0.000 sec)

Database changed
Query OK, 0 rows affected (0.012 sec)

Query OK, 5 rows affected (0.002 sec)
Records: 5  Duplicates: 0  Warnings: 0

+--------+-----------+----------+------------------+
| idUser | login     | password | connectionNumber |
+--------+-----------+----------+------------------+
|      1 | Anderson  | Neo      |                0 |
|      2 | Skywalker | Luke     |                0 |
|      3 | Plissken  | Snake    |                0 |
|      4 | Ripley    | Ellen    |                0 |
|      5 | Bond      | James    |                0 |
+--------+-----------+----------+------------------+
5 rows in set (0.000 sec)

Query OK, 0 rows affected (0.014 sec)

Query OK, 3 rows affected (0.002 sec)
Records: 3  Duplicates: 0  Warnings: 0

+-----------+---------------------+
| idCommand | commandDate         |
+-----------+---------------------+
|         1 | 2020-01-21 12:35:06 |
|         2 | 2020-01-21 12:35:06 |
|         3 | 2020-01-21 12:35:06 |
+-----------+---------------------+
3 rows in set (0.000 sec)

Query OK, 0 rows affected (0.033 sec)

Query OK, 3 rows affected (0.006 sec)
Records: 3  Duplicates: 0  Warnings: 0

+-----------+--------+
| IdCommand | IdUser |
+-----------+--------+
|         1 |      1 |
|         2 |      2 |
|         3 |      1 |
+-----------+--------+
3 rows in set (0.000 sec)

MariaDB [OneToMany]>

Implémentation du mapping JPA.

Pour réaliser une relation d'association de type « One-To-Many » avec table d'association, il faut encore utiliser l'annotation @OneToMany, mais il va falloir la coupler à une annotation @JoinTable afin d'y spécifier les informations utiles à la jointure. Voici un extrait de code relatif à la définition d'une telle relation.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
@Entity @Table(name = "T_Users")
public class User {

    // Autres attributs
    
    @OneToMany
    @JoinTable( name = "T_Commands_Users_Associations",
                joinColumns = @JoinColumn( name = "idUser" ),
                inverseJoinColumns = @JoinColumn( name = "idCommand" ) )
    private List<Command> commands = new ArrayList<>();
            
    // Suite de la classe
    
}
Mise en oeuvre d'une relation de type @OneToMany avec table d'association

Voici le code complet de la classe User.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
 22 
 23 
 24 
 25 
 26 
 27 
 28 
 29 
 30 
 31 
 32 
 33 
 34 
 35 
 36 
 37 
 38 
 39 
 40 
 41 
 42 
 43 
 44 
 45 
 46 
 47 
 48 
 49 
 50 
 51 
 52 
 53 
 54 
 55 
 56 
 57 
 58 
 59 
 60 
 61 
 62 
 63 
 64 
 65 
 66 
 67 
 68 
 69 
 70 
 71 
 72 
 73 
 74 
 75 
 76 
 77 
package fr.koor.webstore.business;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity @Table(name="T_Users")
public class User {

    @Id @GeneratedValue(strategy=GenerationType.IDENTITY)
    private int idUser;
    private String login;
    private String password;
    private int connectionNumber;
    
    @OneToMany
    @JoinTable( name = "T_Commands_Users_Associations",
                joinColumns = @JoinColumn( name = "idUser" ),
                inverseJoinColumns = @JoinColumn( name = "idCommand" ) )
    private List<Command> commands = new ArrayList<>();
    
    public User() { }
    
    public User( String login, String password, int connectionNumber ) {
        super();
        this.setLogin( login );
        this.setPassword( password );
        this.setConnectionNumber( connectionNumber );
    }

    
    public int getIdUser() {
        return idUser;
    }
    
    
    public String getLogin() {
        return login;
    }
    
    public void setLogin(String login) {
        this.login = login;
    }

    public String getPassword() {
        return password;
    }
    
    public void setPassword(String password) {
        this.password = password;
    }
    
    public int getConnectionNumber() {
        return connectionNumber;
    }
    
    public void setConnectionNumber(int connectionNumber) {
        this.connectionNumber = connectionNumber;
    }
    
    public List<Command> getCommands() {
        return commands;
    }
    
    public String toString() {
        return this.idUser + ": " + this.login + "/" + this.password + " - " + this.connectionNumber + " connexion(s)";
    }
    
}
La classe User et son mapping JPA

Nous conservons la classe Command à l'identique de ce que nous avions vu dans le chapitre sur les relations de type « Many-To-One » avec table de jointure. Pour rappel, en revoici de code.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
 22 
 23 
 24 
 25 
 26 
 27 
 28 
 29 
 30 
 31 
 32 
 33 
 34 
 35 
 36 
 37 
 38 
 39 
 40 
 41 
 42 
 43 
 44 
 45 
 46 
 47 
 48 
 49 
 50 
 51 
 52 
 53 
 54 
 55 
 56 
 57 
 58 
 59 
 60 
 61 
 62 
 63 
package fr.koor.webstore.business;

import java.util.Date;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

@Entity  @Table(name="T_Commands")
public class Command {

    @Id @GeneratedValue(strategy=GenerationType.IDENTITY)
    private int idCommand;
    
    @ManyToOne  
    @JoinTable( name = "T_Commands_Users_Associations",
                joinColumns = @JoinColumn( name = "idCommand" ),
                inverseJoinColumns = @JoinColumn( name = "idUser" ) )
    private User user;
    
    private Date commandDate;
    
    
    public Command() {}
    
    public Command( User user, Date commandDate ) {
        this.setUser( user );
        this.setCommandDate( commandDate );
    }

    public int getIdCommand() {
        return idCommand;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }
    
    public Date getCommandDate() {
        return commandDate;
    }
    
    public void setCommandDate(Date commandDate) {
        this.commandDate = commandDate;
    }
    
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append( "Commande de >> " ).append( this.user )
               .append( " - " ).append( this.commandDate ).append( "\n" );
        return builder.toString();
    }   
    
}
La classe Command et son mapping JPA

Test de la relation d'association.

Normalement, vous pouvez récupérer le fichier de configuration JPA proposé plus haut dans ce document. Aucune modification ne devrait être nécessaire, sauf si vous avez changé le nom de la base de données.

Vous devriez aussi pouvoir reprendre les exemples d'utilisation de vos classes précédemment proposés et ils devraient correctement fonctionner. Pour rappel, voici un petit exemple d'utilisation.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
 22 
 23 
 24 
 25 
 26 
 27 
 28 
 29 
 30 
 31 
package fr.koor.webstore;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

import fr.koor.webstore.business.Command;
import fr.koor.webstore.business.User;

public class Console {

    public static void main(String[] args) throws Exception {
        EntityManagerFactory entityManagerFactory = null;
        EntityManager entityManager = null;
        
        try {
        
            entityManagerFactory = Persistence.createEntityManagerFactory("WebStore");
            entityManager = entityManagerFactory.createEntityManager();
        
            User user = entityManager.find( User.class, 1 );            
            for( Command cmd : user.getCommands() ) {
                System.out.println( cmd );
            }
            
        } finally {
            if ( entityManager != null ) entityManager.close();
            if ( entityManagerFactory != null ) entityManagerFactory.close();
        }
    }
}
Utilisation d'une relation de type @OneToMany avec table d'association

Les résultats affichés devraient rester inchangés.

[main] WARN  | org.hibernate.orm.connections.pooling                        | HHH10001002: Using Hibernate built-in connection pool (not for production use!) (DriverManagerConnectionProviderImpl.java:70)
Commande de >> 1: Anderson/Neo - 0 connexion(s) - 2020-01-21 19:22:53.0

Commande de >> 1: Anderson/Neo - 0 connexion(s) - 2020-01-21 19:22:53.0


Mapping d'une relation @ManyToOne Mapping d'une relation @ManyToMany