`` 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.
Pour comprendre cet article, il est recommandé de savoir faire un mapping Hibernate.
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 :
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;
}
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;
}
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 :
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.
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 = « id_fournisseur »)), 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.
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);
}
Dans cet article, nous allons plonger dans le monde d'Hibernate 6, en commençant par reprendre les bases d’Hibernate et ses principaux concepts. Nous explorerons ensuite les nouveautés et les améliorations apportées par Hibernate 6.
Échanger des DTO adaptés à chaque besoin afin de retourner le moins de données possible
Si vous faites des projets informatiques ou que vous êtes simplement passionné, vous avez probablement déjà rencontré la notion « d'intégration continue » ou de « continuous integration ». Process souvent utilisé lors du développement de nos projets, on v