1. 序章

継承は、Javaの重要な概念の1つです。 したがって、ほとんどのドメインモデルがそれを使用しているのは当然のことです。 ただし、残念ながら、この概念はリレーショナルデータベースには存在しないため、継承階層をリレーショナルテーブルモデルにマップする方法を見つける必要があります。

JPAとHibernateは、継承階層をさまざまなテーブルモデルにマップするさまざまな戦略をサポートしています。 私の新しい本HibernateTipsの章を見てみましょう– SingleTable戦略を説明する一般的なHibernateの問題に対する70以上の解決策。 継承階層のすべてのクラスを同じデータベーステーブルにマップします。

Hibernateの他の継承マッピング戦略については、Hibernateのヒントブックで説明しています。 これは、基本および高度なマッピング、ロギング、Java 8サポート、キャッシング、静的および動的に定義されたクエリなどのトピックについて、70を超えるすぐに使用できるレシピを備えたクックブックです。 今週はAmazonでたったの2.99ドルの特別発売価格で入手できます。


2. Hibernateのヒント–継承階層を1つのテーブルにマップする方法

2.1. 問題

私のデータベースには、エンティティの継承階層にマップしたい1つのテーブルが含まれています。 このようなマッピングを定義するにはどうすればよいですか?

2.2. 解決

JPAとHibernateは、エンティティを異なるテーブル構造にマップできるようにする異なる継承戦略をサポートしています。 SingleTable 戦略はその1つであり、エンティティの継承階層を単一のデータベーステーブルにマップします。

SingleTable 戦略の詳細を説明する前に、エンティティモデルを見てみましょう。 作成者は、BooksBlogPostsなど、さまざまな種類のPublicationsを作成できます。 Publication クラスは、BookおよびBlogPostクラスのスーパークラスです。

SingleTable 戦略は、継承階層の3つのエンティティをpublicationテーブルにマップします。

この継承戦略を使用する場合は、スーパークラスに @Inheritance アノテーションを付け、InheritanceType.SINGLE_TABLEstrategyの値として指定する必要があります。属性。

スーパークラスに@DiscriminatorColumnアノテーションを付けて、ディスクリミネーター値の名前を定義することもできます。 Hibernateはこの値を使用して、データベースレコードをマップする必要のあるエンティティを決定します。 次のコードスニペットで行うように、識別子列を定義しない場合、Hibernateおよび他のすべてのJPA実装は列DTYPEを使用します。

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public abstract class Publication {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id", updatable = false, nullable = false)
    private Long id;

    @Version
    private int version;

    private String title;

    private LocalDate publishingDate;
	
    @ManyToMany
    @JoinTable(
      name="PublicationAuthor",
      joinColumns={@JoinColumn(name="publicationId", referencedColumnName="id")},
      inverseJoinColumns={@JoinColumn(name="authorId", referencedColumnName="id")})
    private Set<Author> authors = new HashSet<Author>();

    ...
}

サブクラスはスーパークラスを拡張する必要があり、@Entityアノテーションを付ける必要があります。

JPA仕様では、 @DiscriminatorValue アノテーションを付けて、このエンティティークラスのディスクリミネーター値を定義することも推奨しています。 このアノテーションを指定しない場合、JPA実装はディスクリミネーター値を生成します。

ただし、JPA仕様では、ディスクリミネーター値を生成する方法が定義されていないため、アプリケーションを他のJPA実装に移植できない場合があります。 Hibernateは単純なエンティティ名を識別子として使用します。

@Entity
@DiscriminatorValue("Book")
public class Book extends Publication {

    private int numPages;

    ...
}

SingleTable 戦略では、特定のエンティティを選択したり、ポリモーフィッククエリを実行したり、ポリモーフィックな関連付けをトラバースしたりする場合に、Hibernateが複雑なクエリを生成する必要はありません。

Author a = em.find(Author.class, 1L);
List<Publication> publications = a.getPublications();

すべてのエンティティは同じテーブルに格納され、Hibernateは追加のJOIN句なしでそこからエンティティを選択できます。

15:41:28,379 DEBUG [org.hibernate.SQL] - 
    select
        author0_.id as id1_0_0_,
        author0_.firstName as firstNam2_0_0_,
        author0_.lastName as lastName3_0_0_,
        author0_.version as version4_0_0_ 
    from
        Author author0_ 
    where
        author0_.id=?
15:41:28,384 DEBUG [org.hibernate.SQL] - 
    select
        publicatio0_.authorId as authorId2_2_0_,
        publicatio0_.publicationId as publicat1_2_0_,
        publicatio1_.id as id2_1_1_,
        publicatio1_.publishingDate as publishi3_1_1_,
        publicatio1_.title as title4_1_1_,
        publicatio1_.version as version5_1_1_,
        publicatio1_.numPages as numPages6_1_1_,
        publicatio1_.url as url7_1_1_,
        publicatio1_.DTYPE as DTYPE1_1_1_ 
    from
        PublicationAuthor publicatio0_ 
    inner join
        Publication publicatio1_ 
            on publicatio0_.publicationId=publicatio1_.id 
    where
        publicatio0_.authorId=?

2.3. ソースコード

このHibernateヒントの実行可能なテストケースを含むプロジェクトのダウンロードリンクは、ブックにあります。

2.4. もっと詳しく知る

継承階層のエンティティを複数のデータベーステーブルにマップすることもできます。 これを行う方法については、継承階層を複数のテーブルにマップする方法の章で説明します。

 

3. 概要

このHibernateのヒントで見たように、JPAとHibernateは、継承階層を単一のデータベーステーブルにマップするための簡単なオプションを提供します。 スーパークラスに@Inheritance(strategy = InheritanceType.SINGLE_TABLE)で注釈を付ける必要があり、サブクラスにも @DiscriminatorValue( “Book”)で注釈を付ける必要があります。

あなたは私の新しい本でこのようなより多くのレシピを得ることができます Hibernateのヒント:一般的なHibernateの問題に対する70を超えるソリューション. 基本および高度なマッピング、ロギング、Java 8サポート、キャッシング、静的および動的に定義されたクエリなどのトピックについて、70を超えるすぐに使用できるレシピを提供します。 ほんの数日で、電子ブックを$ 2.99で、ペーパーバックを$12.99で手に入れることができます。 アマゾンで.