1. 序章

Hibernateでは、フィールドの1つを List にすることで、JavaBeanで1対多の関係を表すことができます。

このクイックチュートリアルでは、代わりにMapを使用してこれを行うさまざまな方法について説明します。

2. マップリストとは異なります

マップを使用して1対多の関係を表すことは、キーがあるため、リストとは異なります。

このキーは、エンティティの関係を ternary の関連付けに変換します。ここで、各キーは、単純な値、埋め込み可能なオブジェクト、またはエンティティを参照します。 このため、 Map を使用するには、親エンティティを参照する外部キー(キー、および値)を格納するための結合テーブルが常に必要です。

ただし、この結合テーブルは、主キーが必ずしも親とターゲットへの外部キーであるとは限らないという点で、他の結合テーブルとは少し異なります。代わりに、主キーを複合にします。親への外部キーとマップへのキーである列の。

Map のキーと値のペアには、ValueTypeとEntityTypeの2つのタイプがあります。 次のセクションでは、Hibernateでこれらの関連付けを表す方法を見ていきます。

3. @MapKeyColumnを使用する

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 列を使用し、 Map のキーは、結合テーブルorder_item_mappingitem_name列です。 同様に、 @Column は、マップの値が結合テーブルのprice列に対応することを指定します。

また、 itemPriceMap オブジェクトは値型マップであるため、@ElementCollectionアノテーションを使用する必要があります。

基本的な値タイプのオブジェクトに加えて、@Embeddableオブジェクトも同様の方法でMapの値として使用できます。

4. @MapKeyを使用する

ご存知のとおり、要件は時間の経過とともに変化します。つまり、Itemの属性をitemNameおよび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
}

したがって、変更しましょう地図地図の中に注文エンティティクラス:

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

}

今回は、 @MapKey アノテーションを使用して、Hibernateが追加の列を導入する代わりに Item# itemNameをマップキー列として使用することに注意してください。結合テーブルで。 したがって、この場合、結合テーブル order_item_mapping にはキー列がありません。代わりに、 I[を参照します。 X143X]temの名前。

これはとは対照的です @MapKeyColumn。 いつ @MapKeyColumnを使用します。マップキーは結合テーブルにあります 。 これが、両方のアノテーションを組み合わせて使用してエンティティマッピングを定義できない理由です。

また、 itemMap はエンティティタイプのマップであるため、@OneToManyまたは@ManyToManyを使用して関係に注釈を付ける必要があります。

5. @MapKeyEnumeratedおよび@MapKeyTemporalを使用する

Map キーとして列挙型を指定する場合は常に、@MapKeyEnumeratedを使用します。 同様に、時間値の場合、@MapKeyTemporalが使用されます。 動作は、それぞれ標準の@Enumeratedおよび@Temporalアノテーションと非常によく似ています。

デフォルトでは、これらは @MapKeyColumn に似ており、キー列が結合テーブルに作成されます。永続化されたエンティティにすでに格納されている値を再利用する場合は、さらに、フィールドに@MapKeyのマークを付けます。

6. @MapKeyJoinColumnを使用する

次に、各アイテムの売り手を追跡する必要があるとしましょう。 これを行う1つの方法は、セラーエンティティを追加し、それをアイテムエンティティに関連付けることです。

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

この場合、私たちのユースケースはすべてをグループ化することであると仮定しましょう注文アイテム s by 売り手。 したがって、変更しましょう地図地図

@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 列(マップキー)を結合テーブルorder_item_mappingとともに保持できるようにするためです。 item_id列。 したがって、データベースからデータを読み取るときに、 GROUPBY操作を簡単に実行できます。

7. 結論

この記事では、必要なマッピングに応じて、HibernateでMapを永続化するいくつかの方法について学びました。

いつものように、このチュートリアルのソースコードはGithub上にあります。