Mappe persistenti con Hibernate

1. Introduzione

In Hibernate, possiamo rappresentare relazioni uno-a-molti nei nostri Java bean facendo in modo che uno dei nostri campi sia un List .

In questo breve tutorial, esploreremo vari modi per farlo con una mappa .

2. Mappa s sono diversi da Lista s

Usare una mappa per rappresentare una relazione uno-a-molti è diverso da un elenco perché abbiamo una chiave.

Questa chiave trasforma la nostra relazione di entità in un'associazione ternaria , in cui ogni chiave fa riferimento a un valore semplice o un oggetto incorporabile o un'entità. Per questo motivo, per utilizzare una mappa , avremo sempre bisogno di una tabella di join per archiviare la chiave esterna che fa riferimento all'entità padre: la chiave e il valore.

Ma questa tabella di join sarà leggermente diversa dalle altre tabelle di join in quanto la chiave primaria non sarà necessariamente chiavi esterne al genitore e alla destinazione. Invece, avremo la chiave primaria composta da una chiave esterna al genitore e una colonna che è la chiave della nostra mappa.

La coppia chiave-valore nella mappa può essere di due tipi: tipo di valore e tipo di entità. Nelle sezioni seguenti, esamineremo i modi per rappresentare queste associazioni in Hibernate.

3. Utilizzo di @MapKeyColumn

Supponiamo di avere un'entità Ordine e di voler tenere traccia del nome e del prezzo di tutti gli articoli in un ordine. Quindi, vogliamo introdurre una mappa su ordine che mapperà il nome dell'articolo al suo prezzo:

@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 }

Dobbiamo indicare a Hibernate dove prendere la chiave e il valore. Per la chiave, abbiamo usato la colonna @ MapKey , indicando che la chiave della mappa è la colonna item_name della nostra tabella di join, order_item_mapping . Allo stesso modo, @Column specifica che il valore della mappa corrisponde alla colonna del prezzo della tabella di join.

Inoltre, l' oggetto itemPriceMap è una mappa del tipo di valore, quindi dobbiamo usare l' annotazione @ElementCollection .

Oltre agli oggetti di tipo valore di base, gli oggetti @ Embeddable possono essere utilizzati anche come valori della mappa in modo simile.

4. Utilizzo di @MapKey

Come tutti sappiamo, i requisiti cambiano nel tempo, quindi, ora, supponiamo di dover memorizzare altri attributi di Item insieme a itemName e 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 }

Di conseguenza, cambiamo Map to Map nella classe di 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; }

Nota che questa volta useremo l' annotazione @MapKey in modo che Hibernate utilizzi Item # itemName come colonna della chiave della mappa invece di introdurre una colonna aggiuntiva nella tabella di join. Quindi, in questo caso, il join tavolo order_item_mapping non ha una colonna di chiave - invece, si riferisce al I TEM 's nome.

Questo è in contrasto con @MapKeyColumn. Quando usiamo @MapKeyColumn, la chiave della mappa risiede nella tabella di join . Questo è il motivo per cui non possiamo definire la nostra mappatura di entità utilizzando entrambe le annotazioni insieme.

Inoltre, itemMap è una mappa del tipo di entità, quindi dobbiamo annotare la relazione utilizzando @OneToMany o @ManyToMany .

5. Utilizzo di @MapKeyEnumerated e @MapKeyTemporal

Ogni volta che specifichiamo un'enumerazione come chiave Map , utilizziamo @MapKeyEnumerated . Allo stesso modo, per i valori temporali, viene utilizzato @MapKeyTemporal . Il comportamento è abbastanza simile alle annotazioni standard @Enumerated e @Temporal rispettivamente.

Per impostazione predefinita, sono simili a @MapKeyColumn in quanto verrà creata una colonna chiave nella tabella di join. Se vogliamo riutilizzare il valore già memorizzato nell'entità persistente, dovremmo inoltre contrassegnare il campo con @MapKey .

6. Utilizzo di @MapKeyJoinColumn

Successivamente, diciamo che dobbiamo anche tenere traccia del venditore di ogni articolo. Un modo per farlo è aggiungere un'entità venditore e collegarla alla nostra entità articolo :

@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 }

In questo caso, supponiamo che il nostro caso d'uso è quello di raggruppare tutti Order s' Articolo l da venditore. Quindi, cambiamo mappa in mappa :

@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 }

Dobbiamo aggiungere @MapKeyJoinColumn per ottenere ciò poiché quell'annotazione consente a Hibernate di mantenere la colonna seller_id (la chiave della mappa) nella tabella di join order_item_mapping insieme alla colonna item_id . Quindi, al momento della lettura dei dati dal database, possiamo eseguire facilmente un'operazione GROUP BY .

7. Conclusione

In questo articolo, abbiamo appreso i diversi modi per mantenere la mappa in Hibernate a seconda della mappatura richiesta.

Come sempre, il codice sorgente di questo tutorial può essere trovato su Github.