Hibernate一対多アノテーションチュートリアル

Hibernate One to Many Annotationチュートリアル

1. 前書き

この簡単なHibernateチュートリアルでは、XMLの代わりにJPAアノテーションを使用したone-to-manyマッピングの例を紹介します。

また、双方向の関係とは何か、それらがどのように不整合を引き起こす可能性があるか、ownership スキャンのアイデアがどのように役立つかについても学びます。

参考文献:

Hibernateを使用したSpring Boot

Spring BootとHibernate / JPAを統合するための迅速で実用的な入門書。

Hibernateの識別子の概要

Hibernateでのエンティティの識別子をマッピングする方法を学びます。

2. 説明

簡単に言えば、one-to-many mapping means that one row in a table is mapped to multiple rows in another table.

次のエンティティ関係図を見て、one-to-manyの関連付けを確認しましょう。

image

この例では、カートシステムを実装します。このシステムでは、各カート用のテーブルと各アイテム用の別のテーブルがあります。 One cart can have many items, so here we have a one-to-many mapping.

これがデータベースレベルで機能する方法は、cartテーブルの主キーとしてcart_idがあり、itemsの外部キーとしてcart_id があることです。

そして、コードでそれを行う方法は、@OneToManyを使用することです。

データベース内の関係を反映する方法で、CartクラスをItemsオブジェクトにマッピングしてみましょう。

public class Cart {

    //...

    @OneToMany(mappedBy="cart")
    private Set items;

    //...
}

@ManyToOneを使用してItemsCartへの参照を追加し、これをbidirectionalの関係にすることもできます。 Bidirectionalは、we are able to access items from carts, and also carts from itemsを意味します。

mappedByプロパティは、子クラスの親クラスを表すために使用している変数をHibernateに通知するために使用するものです。

one-to-manyアソシエーションを実装するサンプルHibernateアプリケーションを開発するために、以下のテクノロジーとライブラリーが使用されます。

  • JDK1.8以降

  • Hibernate 5

  • Maven3以降

  • H2データベース

3. セットアップ

3.1. データベースのセットアップ

以下は、CartおよびItemsテーブルのデータベーススクリプトです。 one-to-manyマッピングに外部キー制約を使用します。

CREATE TABLE `Cart` (
  `cart_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`cart_id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

CREATE TABLE `Items` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `cart_id` int(11) unsigned NOT NULL,
  PRIMARY KEY (`id`),
  KEY `cart_id` (`cart_id`),
  CONSTRAINT `items_ibfk_1` FOREIGN KEY (`cart_id`) REFERENCES `Cart` (`cart_id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;

データベースのセットアップが完了しました。Hibernateサンプルプロジェクトの作成に移りましょう。

3.2. Mavenの依存関係

次に、HibernateおよびH2ドライバーの依存関係をpom.xmlファイルに追加します。 Hibernate依存関係はJBossロギングを使用し、推移的な依存関係として自動的に追加されます。

  • Hibernateバージョン5.2.7.Final

  • H2ドライバーバージョン1.4.197

Hibernateの最新バージョンとH2の依存関係については、Maven中央リポジトリにアクセスしてください。

3.3. 休止状態の設定

Hibernateの構成は次のとおりです。


    
        org.h2.Driver
        
        
          jdbc:h2:mem:spring_hibernate_one_to_many
        sa
        org.hibernate.dialect.H2Dialect
        thread
        true
    

3.4. HibernateAnnotationUtilクラス

HibernateAnnotationUtilクラスでは、新しいHibernate構成ファイルを参照する必要があります。

private static SessionFactory sessionFactory;

private SessionFactory buildSessionFactory() {

    ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().
      configure("hibernate-annotation.cfg.xml").build();
    Metadata metadata = new MetadataSources(serviceRegistry).getMetadataBuilder().build();
    SessionFactory sessionFactory = metadata.getSessionFactoryBuilder().build();

    return sessionFactory;
}

public SessionFactory getSessionFactory() {
    if(sessionFactory == null) sessionFactory = buildSessionFactory();
    return sessionFactory;
}

4. モデル

マッピング関連の設定は、モデルクラスのJPAアノテーションを使用して行われます。

@Entity
@Table(name="CART")
public class Cart {

    //...

    @OneToMany(mappedBy="cart")
    private Set items;

    // getters and setters
}

@OneToManyアノテーションは、mappedBy変数のマップに使用されるItemsクラスのプロパティを定義するために使用されることに注意してください。 そのため、Itemsクラスに「cart」という名前のプロパティがあります。

@Entity
@Table(name="ITEMS")
public class Items {

    //...
    @ManyToOne
    @JoinColumn(name="cart_id", nullable=false)
    private Cart cart;

    public Items() {}

    // getters and setters
}

@ManyToOneアノテーションはCartクラス変数に関連付けられていることに注意することが重要です。 @JoinColumnアノテーションは、マップされた列を参照します。

5. 実行中

テストプログラムでは、Hibernateセッションを取得し、one-to-manyアソシエーションを実装するデータベースにモデルオブジェクトを保存するためのmain()メソッドを使用してクラスを作成しています。

sessionFactory = HibernateAnnotationUtil.getSessionFactory();
session = sessionFactory.getCurrentSession();
System.out.println("Session created");

tx = session.beginTransaction();

session.save(cart);
session.save(item1);
session.save(item2);

tx.commit();
System.out.println("Cart ID=" + cart.getId());
System.out.println("item1 ID=" + item1.getId()
  + ", Foreign Key Cart ID=" + item.getCart().getId());
System.out.println("item2 ID=" + item2.getId()
+ ", Foreign Key Cart ID=" + item.getCart().getId());

これはテストプログラムの出力です。

Session created
Hibernate: insert into CART values ()
Hibernate: insert into ITEMS (cart_id)
  values (?)
Hibernate: insert into ITEMS (cart_id)
  values (?)
Cart ID=7
item1 ID=11, Foreign Key Cart ID=7
item2 ID=12, Foreign Key Cart ID=7
Closing SessionFactory

6. @ManyToOne注釈

セクション2で見たように、@ManyToOneアノテーションを使用してmany-to-one関係を指定できます。 many-to-oneマッピングは、このエンティティの多くのインスタンスが別のエンティティの1つのインスタンスにマッピングされることを意味します–many items in one cart.

The @ManyToOne annotation lets us create bidirectional relationships, too.これについては、次のいくつかのサブセクションで詳しく説明します。

6.1. 矛盾と所有権

ここで、CartItemsを参照しているが、ItemsCartour relationship would be unidirectionalを参照していない場合。 The objects would also have a natural consistency.

ただし、この場合、関係は双方向です。bringing in the possibility of inconsistency.

開発者がitem1cartに、item2cart2に追加したいが、cart2と%の間の参照が間違っている状況を想像してみましょう。 (t5)が不整合になります:

Cart cart1 = new Cart();
Cart cart2 = new Cart();

Items item1 = new Items(cart1);
Items item2 = new Items(cart2);
Set itemsSet = new HashSet();
itemsSet.add(item1);
itemsSet.add(item2);
cart1.setItems(itemsSet); // wrong!

上に示したように、item2cart2 を参照しますが、cart2 item2 —を参照せず、それは悪い.です。

How should Hibernate save item2 to the database?item2cart1またはcart2を参照しますか?

関係のowning sideの考え方を使用して、このあいまいさを解決します。所有側に属する参照が優先され、データベースに保存されます。

6.2. 所有側としてのitems

セクション2.9のJPA specificationに記載されているように、it’s a good practice to mark many-to-one side as the owning side.

つまり、Items owning side になり、Cartはその逆になります。これは、以前に行ったこととまったく同じです。

では、どのようにしてこれを達成したのでしょうか?

CartクラスにmappedBy属性を含めることにより、それを逆側としてマークします。

同時に、Items.cartフィールドに@ManyToOneの注釈を付けて、Itemsを所有側にします。

「不整合」の例に戻ると、Hibernateはitem2‘s reference is more important and will save item2‘s reference to the database.

結果を確認しましょう:

item1 ID=1, Foreign Key Cart ID=1
item2 ID=2, Foreign Key Cart ID=2

cartはスニペット内のitem2を参照していますが、item2cart2への参照はデータベースに保存されています。

6.3. 所有側としてのCart

one-to-many側を所有側としてマークし、many-to-one側を逆側としてマークすることもできます。

これは推奨される方法ではありませんが、先に進んで試してみましょう。

以下のコードスニペットは、所有側としてのone-to-many側の実装を示しています。

public class ItemsOIO {

    //  ...
    @ManyToOne
    @JoinColumn(name = "cart_id", insertable = false, updatable = false)
    private CartOIO cart;
    //..
}

public class CartOIO {

    //..
    @OneToMany
    @JoinColumn(name = "cart_id") // we need to duplicate the physical information
    private Set items;
    //..
}

mappedBy要素を削除し、many-to-one @JoinColumninsertableとして設定し、updatablefalseに設定したことに注目してください。

同じコードを実行すると、結果は逆になります。

item1 ID=1, Foreign Key Cart ID=1
item2 ID=2, Foreign Key Cart ID=1

上に示したように、item2cart.に属します

7. 結論

JPAアノテーションを使用してHibernateORMおよびH2データベースとのone-to-many関係を実装するのがいかに簡単であるかを見てきました。

また、双方向の関係と、所有側の概念を実装する方法についても学びました。

このチュートリアルのソースコードはover on GitHubにあります。