JPAで単一のエンティティを複数のテーブルにマッピングする

永続性トップ

Spring5とSpringBoot2の基礎に焦点を当てた新しいLearnSpringコースを発表しました。

>>コースをチェックしてください

1.はじめに

JPAにより、Javaアプリケーションからのリレーショナルデータベースモデルの処理が簡単になります。すべてのテーブルを単一のエンティティクラスにマップすると、作業は簡単になります。ただし、エンティティとテーブルを異なる方法でモデル化する理由がある場合があります。

  • フィールドの論理グループを作成する場合、複数のクラスを1つのテーブルにマップできます
  • 継承が含まれる場合、クラス階層をテーブル構造にマップできます
  • 関連するフィールドが複数のテーブルに散在していて、それらのテーブルを単一のクラスでモデル化したい場合

この短いチュートリアルでは、この最後のシナリオに取り組む方法を説明します。

2.データモデル

たとえば、レストランを経営していて、提供するすべての食事に関するデータを保存したいとします。

  • 名前
  • 説明
  • 価格
  • どんなアレルゲンが含まれているのか

考えられるアレルゲンはたくさんあるので、このデータセットをグループ化します。さらに、次のテーブル定義を使用してこれをモデル化します。

次に、標準のJPAアノテーションを使用してこれらのテーブルをエンティティにマップする方法を見てみましょう。

3.複数のエンティティを作成する

最も明白な解決策は、両方のクラスのエンティティを作成することです。

Mealエンティティを定義することから始めましょう:

@Entity @Table(name = "meal") class Meal { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") Long id; @Column(name = "name") String name; @Column(name = "description") String description; @Column(name = "price") BigDecimal price; @OneToOne(mappedBy = "meal") Allergens allergens; // standard getters and setters }

次に、Allergensエンティティを追加します。

@Entity @Table(name = "allergens") class Allergens { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "meal_id") Long mealId; @OneToOne @PrimaryKeyJoinColumn(name = "meal_id") Meal meal; @Column(name = "peanuts") boolean peanuts; @Column(name = "celery") boolean celery; @Column(name = "sesame_seeds") boolean sesameSeeds; // standard getters and setters }

上記の例では、meal_idが主キーであると同時に外部キーでもあることがわかります。つまり、@ PrimaryKeyJoinColumnを使用して1対1の関係列を定義する必要があります。

ただし、このソリューションには2つの問題があります。

  • 私たちは常に食事のためにアレルゲンを保存したいと思っています、そしてこの解決策はこの規則を強制しません
  • 食事とアレルゲンのデータは論理的に一緒に属しているため、複数のテーブルを作成した場合でも、この情報を同じJavaクラスに格納したい場合があります。

最初の問題に対する1つの可能な解決策は、Mealエンティティのアレルゲンフィールドに@NotNullアノテーションを追加することですアレルゲンヌルの場合、JPAは食事を持続させません。

ただし、これは理想的なソリューションではありません。アレルゲンなしで食事続けようとする機会さえない、より制限的なものが必要です

4. @ SecondaryTableを使用して単一のエンティティを作成する

@SecondaryTableアノテーションを使用して、異なるテーブルに列があることを指定する単一のエンティティを作成できます。

@Entity @Table(name = "meal") @SecondaryTable(name = "allergens", pkJoinColumns = @PrimaryKeyJoinColumn(name = "meal_id")) class Meal { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") Long id; @Column(name = "name") String name; @Column(name = "description") String description; @Column(name = "price") BigDecimal price; @Column(name = "peanuts", table = "allergens") boolean peanuts; @Column(name = "celery", table = "allergens") boolean celery; @Column(name = "sesame_seeds", table = "allergens") boolean sesameSeeds; // standard getters and setters }

舞台裏では、JPAはプライマリテーブルとセカンダリテーブルを結合し、フィールドにデータを入力します。このソリューションは@OneToOne関係に似ていますが、このようにして、すべてのプロパティを同じクラスに含めることができます。

セカンダリテーブルに列がある場合は、@ Columnアノテーションのtable引数を使用して指定する必要があることに注意してください列がプライマリテーブルにある場合、JPAはデフォルトでプライマリテーブルの列を検索するため、テーブル引数を省略できます。

また、@ SecondaryTablesに埋め込むと、複数のセカンダリテーブルを持つことができることに注意してください。または、Java 8から、繰り返し可能なアノテーションであるため、エンティティに複数の@SecondaryTableアノテーションを付けることができます。

5. @ SecondaryTable@Embeddedの組み合わせ

これまで見てきたように、@ SecondaryTableは複数のテーブルを同じエンティティにマップします。我々はまた、ことを知っている@Embeddedと@埋め込みが反対をし、複数のクラスに単一のテーブルをマッピングします。

@SecondaryTable@Embeddedおよび@Embeddableと組み合わせると何が得られるか見てみましょう:

@Entity @Table(name = "meal") @SecondaryTable(name = "allergens", pkJoinColumns = @PrimaryKeyJoinColumn(name = "meal_id")) class Meal { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") Long id; @Column(name = "name") String name; @Column(name = "description") String description; @Column(name = "price") BigDecimal price; @Embedded Allergens allergens; // standard getters and setters } @Embeddable class Allergens { @Column(name = "peanuts", table = "allergens") boolean peanuts; @Column(name = "celery", table = "allergens") boolean celery; @Column(name = "sesame_seeds", table = "allergens") boolean sesameSeeds; // standard getters and setters }

これは、@ OneToOneを使用して見たものと同様のアプローチです。ただし、いくつかの利点があります。

  • JPAは2つのテーブルを一緒に管理するため、両方のテーブルに各食事の行があることを確認できます。
  • また、必要な構成が少ないため、コードは少し単純です。

それでも、この1対1のようなソリューションは、2つのテーブルのIDが一致する場合にのみ機能します。

Allergensクラスを再利用する場合は、@ AttributeOverrideを使用してMealクラスのセカンダリテーブルの列を定義した方がよいことに注意してください

6.結論

この短いチュートリアルでは、@ SecondaryTableJPAアノテーションを使用して複数のテーブルを同じエンティティにマップする方法を説明しました。

また、@ SecondaryTable@Embeddedおよび@Embeddableと組み合わせて、1対1のような関係を得る利点も確認しました。

いつものように、例はGitHubで入手できます。

永続性の底

Spring5とSpringBoot2の基礎に焦点を当てた新しいLearnSpringコースを発表しました。

>>コースをチェックしてください