Hibernateでのマップの永続化

1. 前書き

Hibernateでは、フィールドの1つを_List_にすることで、Java Beanでlink:/hibernate-one-to-many[1対多の関係]を表すことができます。
このクイックチュートリアルでは、代わりに_Map_を使用してこれを行うさまざまな方法を検討します。

2.  MapsはListsとは異なります

_Map_を使用して1対多の関係を表すことは、キーがあるため_List_とは異なります。
このキーは、エンティティの関係を_ternary_アソシエーションに変換します。各キーは、単純な値、埋め込み可能なオブジェクト、またはエンティティを参照します。 このため、_Map_を使用するには、親エンティティを参照する外部キー(キーと値)を保存する*結合テーブルが常に必要です。*
ただし、この結合テーブルは他の結合テーブルとは少し異なります。主キーは必ずしも親とターゲットの外部キーではありません。*代わりに、主キーは外部キーの複合になります_Map._のキーである親と列に
_Map_のキーと値のペアには、https://javabydeveloper.com/hibernate-entity-types-vs-value-types/ [値の種類とエンティティの種類]の2つのタイプがあります。 次のセクションでは、これらの関連付けをHibernateで表す方法について説明します。

3. _ @ MapKeyColumn_の使用

_Order_エンティティがあり、注文に含まれるすべてのアイテムの名前と価格を追跡するとします。 したがって、*アイテムの名前をその価格にマッピングする_Map <String、Double> _を_Order_に導入する必要があります:*
@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<String, Double> itemPriceMap;

    // standard getters and setters
}
*キーと値を取得する場所をHibernateに示す必要があります。*キーについては、** @ _ MapKey _ ** __ * Column *、__を使用して、_Map_â€〜sキーが_item_name_列であることを示します。結合テーブル、_order_item_mapping_。 同様に、__ @ Column ___は、__Mapの__valueが結合テーブルの__price __columnに対応することを指定します。
また、__ itemPriceMap __objectは値型マップであるため、__ @ ElementCollection __annotationを使用する必要があります。
基本的な値タイプのオブジェクトに加えて、https://docs.jboss.org/hibernate/jpa/2.1/api/javax/persistence/Embeddable.html [@_Embeddable_]オブジェクトを_Map_âの値として使用することもできます。同様の方法。

4. _ @ MapKey_の使用

ご存じのとおり、要件は時間の経過とともに変化します。したがって、今度は、__ Item __alongの属性を__itemName __and _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
}
したがって、__Order __entityクラスの_Map <String、Double> _を__Map <String、Item> __に変更しましょう。
@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<String, Item> itemMap;

}
今回は、結合テーブルに追加の列を導入する代わりに、Hibernateが_Item#itemName_をマップキー列として使用するように、_ @ MapKey_アノテーションを使用することに注意してください。 そのため、この場合、**結合テーブル** __ * order_item_mapping * __ *にはキー列がありません* —代わりに、_Item_â€〜の名前を参照します。
これは、__ @ MapKeyColumnとは対照的です。 __ ** __ @ MapKeyColumnを使用する場合、__マップキーは結合テーブルに存在します*。 これが、両方のアノテーションを組み合わせて使用​​するエンティティマッピングを定義できない理由です*。
また、__itemMap __はエンティティタイプマップであるため、link:/hibernate-one-to-many[[email protected]_]または_https://www.baeldung.com/を使用して関係に注釈を付ける必要があります。 hibernate-many-to-many [@ManyToMany] _。

5. _ @ MapKeyEnumerated_および_ @ MapKeyTemporal_の使用

_Map_キーとして列挙型を指定するたびに、_ @ MapKeyEnumerated_を使用します。 同様に、一時的な値には、_ @ MapKeyTemporal_が使用されます。 動作は、標準のhttps://docs.jboss.org/hibernate/jpa/2.1/api/javax/persistence/Enumerated.html[[email protected]_]およびhttps://docs.jboss.org/hibernateと非常に似ています。 /jpa/2.1/api/javax/persistence/Temporal.html[[email protected]_]アノテーション。
デフォルトでは、これらは_ @ MapKeyColumn_に似ています。*結合テーブルにキー列が作成されます。*永続化されたエンティティに既に保存されている値を再利用する場合は、フィールドに_ @ MapKey_を追加でマークする必要があります。

6. _ @ MapKeyJoinColumn_の使用

次に、各アイテムの売り手を追跡する必要があるとしましょう。 これを行う1つの方法は、_Seller_エンティティを追加し、それを_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
}
この場合、ユースケースがすべての_Order_â€〜s __Item__sを˜__Sellerでグループ化することであると仮定しましょう。 __したがって、_Map <String、Item> _を_Map <Seller、Item> _に変更しましょう。
@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<Seller, Item> sellerItemMap;

    // standard getters and setters

}
これを実現するために_ @ MapKeyJoinColumn_を追加する必要があります。このアノテーションにより、Hibernateが__seller_id __column(マップキー)を__item_id __columnとの結合テーブル__order_item_mapping __alongに保持できるためです。 したがって、データベースからデータを読み取るときに、, _ GROUP BY_操作を簡単に実行できます。

7. 結論

この記事では、必要なマッピングに応じてHibernateで_Map_を永続化するいくつかの方法について学びました。
いつものように、このチュートリアルのソースコードはhttps://github.com/eugenp/tutorials/tree/master/persistence-modules/hibernate-mapping[over Github]にあります。