Dans cet article


Offres d'emploi

Tags

Les clés primaires composées avec Hibernate 4

Les clés primaires composées avec Hibernate 4

Dans une base de données relationnelle, la plupart des tables possèdent une clé primaire appliquée sur un seul champ. Cependant, une clé primaire peut s’appliquer à plusieurs d’entre eux : on parle de clé primaire composée.

1. Les prérequis

Pour comprendre cet article, il est recommandé de savoir faire un mapping Hibernate.

2. Structure de l’entité

L’entité présentera la particularité de posséder, à la place de l’annotation @Id habituelle, une Embedded key, grâce à l’annotation @EmbeddedId

Pour cet article, nous utiliserons le cas d’exemple suivant :

  • Une table FOURNISSEUR , enregistrant la liste des fournisseurs d’une société ;
  • Une table DEPARTEMENT, enregistrant la liste des départements (français) ;
  • Une table de jointure _FOURNISSEURDEPARTEMENT; enregistrant les liens Fournisseur-Département, permettant de connaître la zone de livraison d’un fournisseur. Un champ supplémentaire VARCHAR informations_complementaires permettra de renseigner des données complémentaires sur la zone de livraison.

2.1. La classe de l’Embedded Key

La particularité de cette classe est de se voir attribuer l’annotation @Embeddable (exportable). Cette classe ne comportera que les champs de la clé primaire (dans notre exemple, il s’agira de ‘id_fournisseur’ et ‘id_departement’ de la table de jointure).

@Embeddable
public class FournisseurDepartementPK implements Serializable{
private static final long serialVersionUID = 1L;

    @Column(name="id_fournisseur")
    private int idFournisseur;

    @Column(name="id_departement")
    private int idDepartement;

    // Constructeur et getters/setters
}

Il est fortement conseillé de surcharger les méthodes equals() et hashcode() de cette classe.

Le metamodel associé est le même que d’habitude, à savoir

@StaticMetamodel(FournisseurDepartementPK.class)
public abstract class FournisseurDepartementPK_{

    public static volatile SingularAttribute<
FournisseurDepartementPK, Integer> idFournisseur;
    public static volatile SingularAttribute<
FournisseurDepartementPK, Integer> idDepartement;
}

2.2 L’entité

Comme précisé en introduction, le type de la clé primaire ne sera pas le même que d’habitude, et ses annotations seront également changées.

Pour rappel, voici comment se présente l’attribut dans une entité d’une table à clé primaire sur un seul champ.

@Entity
@Table(name = "fournisseur")
public class Fournisseur implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="id_fournisseur")
    private int idFournisseur;

    // Constructeur et getters/setters
}

Et voici comment se présente l’entité qui nous concerne :

@Entity
@Table(name="fournisseur_departement")
public class FournisseurDepartement implements Serializable{
    private static final long serialVersionUID = 1L;

   @EmbeddedId
   private FournisseurDepartementPK id;

   // Constructeur, getters et setter
}

Le métamodèle, quand à lui, se présente ainsi :

@StaticMetamodel(FournisseurDepartement.class)

public abstract class FournisseurDepartement_{

    public static volatile SingularAttribute<
FournisseurDepartement, FournisseurDepartementPK> id;
}

3. Le lien avec les objets de la clé primaire

L’entité, dans sa forme actuelle, ne permet pas de faire le lien avec la classe Fournisseur ou la classe Département. Pourtant, cela est possible. voici deux méthodes pour le faire :

3.1. Attributs dans l’entité

La méthode la plus simple est d’ajouter les objets associés à la clé primaire directement dans l’entité, dans notre cas : Fournisseur et Departement.

Cependant, afin d’éviter qu’Hibernate considère ces objets comme des entités à persister, il faut renseigner de nouveaux attributs dans les annotations.

Voici ce que cela donnerait :

@Entity
@Table(name = "fournisseur_departement")
public class FournisseurDepartement implements Serializable {
    private static final long serialVersionUID = 1L;

     @EmbeddedId
     private FournisseurDepartementPK id;

    //bi-directional many-to-one association to FournisseurDepartement
     @ManyToOne(fetch = FetchType.LAZY)
     @JoinColumn(name = "id_fournisseur", insertable = false, updatable = false)
     private Fournisseur fournisseur;

     // bi-directional many-to-one association to FournisseurDepartement
     @ManyToOne(fetch = FetchType.LAZY)
     @JoinColumn(name = "id_departement", insertable = false, updatable = false)
     private Departement departement;
            
     @Column(name = "informations_complementaires")
     private String informationsComplementaires;

     // Constructeur, getters et setters
 }

Grâce aux attributs ‘insertable’ et ‘updatable’ passés à false, Hibernate ne les persistera pas.

Ainsi, le fournisseur et le departement pourront être ajoutés normalement au métamodèle.

 

3.2 L’annotation @AttributeOverrides

Dans le cas où les attributs composant la clé primaire multiple ne sont pas surchargés dans l’entité, il est impossible de mapper correctement une dépendance fonctionnelle de type @OneToMany vers cette entité.

Exemple : dans la classe Fournisseur, si l’on mappe une liste de FournisseurDepartement, on ne peut pas renseigner l’attribut mappedBy de l’annotation @OneToMany, puisqu’aucun champ de la classe FournisseurDepartement ne correspond à notre cible :

@Entity
@Table(name = "fournisseur")
public class Fournisseur implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="id_fournisseur")
    private int idFournisseur;

    @OneToMany(mappedBy = "???")
    private List fournisseurDepartements;

    // Constructeur et getters/setters
}

L’attibuteOverrides permet d’accéder au fournisseur et au département sans ajouter les objets concernés à l’entité FournisseurDepartement.

Voici comment cela se présente

@Entity
@Table(name = "fournisseur_departement")
public class FournisseurDepartement implements Serializable {
    private static final long serialVersionUID = 1L;

    @EmbeddedId
    @AttributeOverrides({
        @AttributeOverride(name = "id.fournisseur", column = @Column(name = "id_fournisseur")),
	@AttributeOverride(name = "id.departement", column = @Column(name = "id_departement")) })
    private FournisseurDepartementPK id;

    @Column(name = "informations_complementaires")
    private String informationsComplementaires;
    
    // Constructeur, getters et setters
}

L’annotation @AttributeOverrides permet de renseigner toutes les colonnes de la clé primaire. Chacune des colonnes est renseignée à l’aide d’une ligne d’annotation @AttributeOverride.

Comme on peut le voir, on retrouve, dans _@AttributeOverride(name = « id.fournisseur », column = @Column(name = « idfournisseur »)), le id.fournisseur, que l’on va renseigner dans le @OneToMany de la classe Fournisseur.

@Entity
@Table(name = "fournisseur")
public class Fournisseur implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="id_fournisseur")
    private int idFournisseur;

    @OneToMany(mappedBy = "id.fournisseur")
    private List fournisseurDepartements;

    // Constructeur et getters/setters
}

Le fonctionnement est le même pour la classe Departement.

Grâce à cette méthode, il n’est plus nécessaire de rajouter les entités Fournisseur et Departement dans l’entité FournisseurDepartement pour y avoir accès.

De même, il est important de noter que ces entités ne seront, de fait, pas à ajoutées au metamodel.

4. Récupérer une instance de l’entité par l’id (getById)

Habituellement, la méthode permettant de récupérer une entité par l’id utilise un objet de type primitif (la plupart du temps, numérique). Voici un rappel du fonctionnement :

public Fournisseur getFournisseurById(int pIdFournisseur) {
    return entityManager.find(Fournisseur.class, pIdFournisseur);
}

Dans notre cas, notre clé primaire n’est pas constituée d’un, mais de plusieurs champs. Cependant, ici, il s’agit d’une Embedded key, donc un objet.

Il suffit donc d’utiliser l’Embedded key. Voici comme cela se présente :

public FournisseurDepartement getFournisseurDepartementById(FournisseurDepartementPK pFournisseurDepartementPK) {
    return entityManager.find(FournisseurDepartement.class,pFournisseurDepartementPK);
}

L'équipe AXOPEN

Voir aussi les articles suivants

La clause GROUP BY en Hibernate 4

La clause GROUP BY en Hibernate 4

Le 27/11/2013 par Robin Chauvin

Comment utiliser la clause SQL GROUP BY avec Hibernate 4 ? Rappel théorique En SQL, lorsqu’une requête possède la clause GROUP BY, les champs mentionnés dans la clause SELECT ne peuvent être que : une colonne sur laquelle porte le GROUP BY ; une fonction d’agrégat (qui, elle, peut porter sur n’importe quelle colonne). En clair, je ne peux pas mentionner dans mon SELECT une colonne sur laquelle je ne groupe pas.
Lire l'article

Hibernate 4 et les fonctions d&rsquo;agrégat

Hibernate 4 et les fonctions d’agrégat

Le 26/11/2013 par Robin Chauvin

En SQL, les fonctions d’agrégat sont les fonctions COUNT(), SUM(), MIN(), MAX() et AVG(). Comment réaliser des requêtes avec de telles fonctions dans la clause SELECT en Hibernate 4 ? Théorie Si vous n’êtes pas familier avec Hibernate 4, reportez-vous préalablement à l’article  ». Les fonctions d’agrégats sont fournies par le CriteriaBuilder. Il suffit donc de passer en paramètre à votre fonction select() non pas un champ ou un Root mais la fonction d’agrégat.
Lire l'article

Le multiselect avec Hibernate 4

Le multiselect avec Hibernate 4

Le 05/11/2013 par Robin Chauvin

Nous avons vu dans l’article « Les requêtes avec Hibernate 4 » comment réaliser une requête ramenant soit un champ soit tous les champs (SELECT *). Voyons à présent comment ramener plusieurs champs mais pas tous. Le code côté Hibernate Pour ce faire, nous avons recours à la fonction multiselect() de la classe CriteriaQuery. Cette fonction prend en paramètres soit une List de champs, soit n paramètres représentant des champs ou des fonctions.
Lire l'article