Persistierende Karten mit Ruhezustand

1. Einleitung

Im Ruhezustand können wir Eins-zu-Viele-Beziehungen in unseren Java-Beans darstellen, indem eines unserer Felder eine Liste ist .

In diesem kurzen Tutorial werden wir verschiedene Möglichkeiten untersuchen, dies stattdessen mit einer Karte zu tun .

2. Karte s unterscheiden sich von Liste s

Die Verwendung einer Karte zur Darstellung einer Eins-zu-Viele-Beziehung unterscheidet sich von einer Liste, da wir einen Schlüssel haben.

Dieser Schlüssel verwandelt unsere Entitätsbeziehung in eine ternäre Zuordnung, wobei sich jeder Schlüssel auf einen einfachen Wert oder ein einbettbares Objekt oder eine Entität bezieht. Aus diesem Grund benötigen wir zur Verwendung einer Map immer eine Join-Tabelle, um den Fremdschlüssel zu speichern, der auf die übergeordnete Entität verweist - den Schlüssel und den Wert.

Diese Verknüpfungstabelle unterscheidet sich jedoch ein wenig von anderen Verknüpfungstabellen darin, dass der Primärschlüssel nicht unbedingt Fremdschlüssel für das übergeordnete Element und das Ziel sind. Stattdessen besteht der Primärschlüssel aus einem Fremdschlüssel für das übergeordnete Element und einer Spalte, die der Schlüssel für unsere Karte ist.

Es kann zwei Arten von Schlüssel-Wert-Paaren in der Karte geben : Werttyp und Entitätstyp. In den folgenden Abschnitten werden wir uns ansehen, wie diese Assoziationen im Ruhezustand dargestellt werden können.

3. Verwenden von @MapKeyColumn

Angenommen, wir haben eine Auftragsentität und möchten den Namen und den Preis aller Artikel in einer Bestellung verfolgen. Daher möchten wir eine Map to Order einführen, die den Namen des Artikels dem Preis zuordnet:

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

Wir müssen dem Ruhezustand mitteilen, wo der Schlüssel und der Wert zu erhalten sind. Für den Schlüssel, haben wir @ verwendet MapKey Spalte , die anzeigt , dass die Karte ‚s Schlüssel der ist item_name Spalte unserer Join - Tabelle, order_item_mapping . In ähnlicher Weise gibt @Column an, dass der Wert der Karte der Preisspalte der Join-Tabelle entspricht.

Außerdem ist das itemPriceMap- Objekt eine Werttypzuordnung , daher müssen wir die Annotation @ElementCollection verwenden .

Neben Objekten mit grundlegenden Werttypen können @ Embeddable- Objekte auf ähnliche Weise auch als Map -Werte verwendet werden.

4. Verwenden von @MapKey

Wie wir alle wissen, ändern sich die Anforderungen im Laufe der Zeit. Nehmen wir also an, wir müssen einige weitere Attribute von Item zusammen mit itemName und itemPrice speichern :

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

Ändern Sie dementsprechend Map in Map in der Entitätsklasse 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; }

Beachten Sie, dass wir dieses Mal die Annotation @MapKey verwenden, damit Hibernate Item # itemName als Map-Key-Spalte verwendet, anstatt eine zusätzliche Spalte in die Join-Tabelle einzufügen . Also, in diesem Fall kommen die Tabelle order_item_mapping keine Schlüsselspalte enthält - stattdessen es bezieht sich auf die ich tem den Namen.

Dies steht im Gegensatz zu @MapKeyColumn. Wenn wir @MapKeyColumn verwenden, befindet sich der Map-Schlüssel in der Join-Tabelle . Dies ist der Grund, warum wir unsere Entitätszuordnung nicht definieren können, indem wir beide Anmerkungen zusammen verwenden.

Außerdem ist itemMap eine Entitätstypzuordnung, daher müssen wir die Beziehung mit @OneToMany oder @ManyToMany kommentieren .

5. Verwenden von @MapKeyEnumerated und @MapKeyTemporal

Immer wenn wir eine Aufzählung als Map- Schlüssel angeben , verwenden wir @MapKeyEnumerated . In ähnlicher Weise wird für zeitliche Werte @MapKeyTemporal verwendet. Das Verhalten ist den Standardanmerkungen @Enumerated und @Temporal ziemlich ähnlich .

Standardmäßig ähneln diese @MapKeyColumn , da in der Join-Tabelle eine Schlüsselspalte erstellt wird. Wenn wir den bereits in der persistierten Entität gespeicherten Wert wiederverwenden möchten, sollten wir das Feld zusätzlich mit @MapKey markieren .

6. Verwenden von @MapKeyJoinColumn

Nehmen wir als nächstes an, wir müssen auch den Verkäufer jedes Artikels im Auge behalten. Eine Möglichkeit, dies zu tun, besteht darin, eine Verkäuferentität hinzuzufügen und diese mit unserer Artikelentität zu verknüpfen:

@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 diesem Fall lassen Sie sich unseren Anwendungsfall übernehmen zu Gruppe ist alles bestellen ‚s Artikel von s Verkäufern. Ändern wir daher Map in 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 }

Wir müssen @MapKeyJoinColumn hinzufügen , um dies zu erreichen, da diese Annotation es Hibernate ermöglicht, die Spalte seller_id (den Zuordnungsschlüssel) in der Verknüpfungstabelle order_item_mapping zusammen mit der Spalte item_id beizubehalten . Zum Zeitpunkt des Lesens der Daten aus der Datenbank können wir also problemlos eine GROUP BY- Operation ausführen .

7. Fazit

In diesem Artikel haben wir die verschiedenen Möglichkeiten kennengelernt, die Map im Ruhezustand abhängig von der erforderlichen Zuordnung beizubehalten.

Der Quellcode dieses Tutorials befindet sich wie immer über Github.