Cartes persistantes avec Hibernate

1. Introduction

Dans Hibernate, nous pouvons représenter des relations un-à-plusieurs dans nos beans Java en faisant en sorte que l'un de nos champs soit une liste .

Dans ce rapide didacticiel, nous allons explorer différentes façons de le faire avec une carte à la place.

2. Carte s sont différents de la liste de

L'utilisation d'une carte pour représenter une relation un-à-plusieurs est différente d'une liste car nous avons une clé.

Cette clé transforme notre relation d'entité en une association ternaire , où chaque clé fait référence à une valeur simple ou à un objet intégrable ou à une entité. Pour cette raison, pour utiliser un Map , nous aurons toujours besoin d' une table de jointure pour stocker la clé étrangère qui référence l'entité parente - la clé et la valeur.

Mais cette table de jointure sera un peu différente des autres tables de jointure en ce que la clé primaire ne sera pas nécessairement des clés étrangères vers le parent et la cible. Au lieu de cela, la clé primaire sera un composite d'une clé étrangère vers le parent et d'une colonne qui est la clé de notre Map.

La paire clé-valeur dans la carte peut être de deux types: Type de valeur et Type d'entité. Dans les sections suivantes, nous examinerons les moyens de représenter ces associations dans Hibernate.

3. Utilisation de @MapKeyColumn

Disons que nous avons une entité Order et que nous voulons garder une trace du nom et du prix de tous les articles d'une commande. Donc, nous voulons introduire une carte à ordre qui tracera le nom de l'élément à son prix:

@Entity @Table(name = "orders") public class Order { @Id @GeneratedValue @Column(name = "id") private int id; @ElementCollection @CollectionTable(name = "order_item_mapping", joinColumns = {@JoinColumn(name = "order_id", referencedColumnName = "id")}) @MapKeyColumn(name = "item_name") @Column(name = "price") private Map itemPriceMap; // standard getters and setters }

Nous devons indiquer à Hibernate où obtenir la clé et la valeur. Pour la clé, nous avons utilisé @ MapKey Column , indiquant que la clé de la carte est la colonne nom_élément de notre table de jointure, order_item_mapping . De même, @Column spécifie que la valeur de la carte correspond à la colonne de prix de la table de jointure.

De plus, l' objet itemPriceMap est une mappe de type valeur, nous devons donc utiliser l' annotation @ElementCollection .

En plus des objets de type valeur de base, les objets @ Embeddable peuvent également être utilisés comme valeurs de Map de la même manière.

4. Utilisation de @MapKey

Comme nous le savons tous, les exigences changent avec le temps - alors, maintenant, disons que nous devons stocker d'autres attributs de Item avec itemName et itemPrice :

@Entity @Table(name = "item") public class Item { @Id @GeneratedValue @Column(name = "id") private int id; @Column(name = "name") private String itemName; @Column(name = "price") private double itemPrice; @Column(name = "item_type") @Enumerated(EnumType.STRING) private ItemType itemType; @Temporal(TemporalType.TIMESTAMP) @Column(name = "created_on") private Date createdOn; // standard getters and setters }

En conséquence, changeons Map en Map dans la classe d'entité Order :

@Entity @Table(name = "orders") public class Order { @Id @GeneratedValue @Column(name = "id") private int id; @OneToMany(cascade = CascadeType.ALL) @JoinTable(name = "order_item_mapping", joinColumns = {@JoinColumn(name = "order_id", referencedColumnName = "id")}, inverseJoinColumns = {@JoinColumn(name = "item_id", referencedColumnName = "id")}) @MapKey(name = "itemName") private Map itemMap; }

Notez que cette fois, nous utiliserons l' annotation @MapKey afin qu'Hibernate utilise Item # itemName comme colonne de clé de carte au lieu d'introduire une colonne supplémentaire dans la table de jointure. Donc, dans ce cas, la table de jointure order_item_mapping n'a pas de colonne clé - à la place, elle fait référence au nom de l' I tem .

Cela contraste avec @MapKeyColumn. Lorsque nous utilisons @MapKeyColumn, la clé de mappage réside dans la table de jointure . C'est la raison pour laquelle nous ne pouvons pas définir notre mappage d'entité en utilisant les deux annotations conjointement.

En outre, itemMap est une carte de type d'entité, nous devons donc annoter la relation en utilisant @OneToMany ou @ManyToMany .

5. Utilisation de @MapKeyEnumerated et @MapKeyTemporal

Chaque fois que nous spécifions une énumération comme clé Map , nous utilisons @MapKeyEnumerated . De même, pour les valeurs temporelles, @MapKeyTemporal est utilisé. Le comportement est assez similaire aux annotations standard @Enumerated et @Temporal respectivement.

Par défaut, ils sont similaires à @MapKeyColumn en ce qu'une colonne clé sera créée dans la table de jointure. Si nous voulons réutiliser la valeur déjà stockée dans l'entité persistante, nous devons également marquer le champ avec @MapKey .

6. Utilisation de @MapKeyJoinColumn

Ensuite, disons que nous devons également suivre le vendeur de chaque article. Une façon de procéder consiste à ajouter une entité Vendeur et à la lier à notre entité Item :

@Entity @Table(name = "seller") public class Seller { @Id @GeneratedValue @Column(name = "id") private int id; @Column(name = "name") private String sellerName; // standard getters and setters }
@Entity @Table(name = "item") public class Item { @Id @GeneratedValue @Column(name = "id") private int id; @Column(name = "name") private String itemName; @Column(name = "price") private double itemPrice; @Column(name = "item_type") @Enumerated(EnumType.STRING) private ItemType itemType; @Temporal(TemporalType.TIMESTAMP) @Column(name = "created_on") private Date createdOn; @ManyToOne(cascade = CascadeType.ALL) @JoinColumn(name = "seller_id") private Seller seller; // standard getters and setters }

Dans ce cas, supposons que notre cas d' utilisation est de regrouper tous ordre de l' article est par le vendeur. Par conséquent, changeons Map en Map :

@Entity @Table(name = "orders") public class Order { @Id @GeneratedValue @Column(name = "id") private int id; @OneToMany(cascade = CascadeType.ALL) @JoinTable(name = "order_item_mapping", joinColumns = {@JoinColumn(name = "order_id", referencedColumnName = "id")}, inverseJoinColumns = {@JoinColumn(name = "item_id", referencedColumnName = "id")}) @MapKeyJoinColumn(name = "seller_id") private Map sellerItemMap; // standard getters and setters }

Nous devons ajouter @MapKeyJoinColumn pour y parvenir depuis cette annotation permet à Hibernate de garder la seller_id colonne (la touche carte) dans la table de jointure order_item_mapping avec la item_id colonne. Ainsi donc, au moment de la lecture des données de la base de données, nous pouvons facilement effectuer une opération GROUP BY .

7. Conclusion

Dans cet article, nous avons découvert les différentes façons de conserver Map dans Hibernate en fonction du mappage requis.

Comme toujours, le code source de ce tutoriel peut être trouvé sur Github.