JPAにおける一対一の関係

JPAでの1対1の関係

1. 前書き

このチュートリアルでは、JPAで1対1のマッピングを作成するさまざまな方法を見ていきます。

Hibernateフレームワークの基本を理解する必要があるため、追加の背景についてはGuide to Hibernate 5 with Springを確認してください。

参考文献:

JPA / Hibernateカスケードタイプの概要

JPA / Hibernateカスケードタイプの簡単で実用的な概要。

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

このチュートリアルでは、実用的な例を使用して、JPAアノテーションを使用した1対多のマッピングについて説明します。

2. 説明

ユーザー管理システムを構築していて、上司からユーザーごとに郵送先住所を保存するように依頼されたとします。 ユーザーには1つの住所があり、住所には1人のユーザーしか関連付けられていません。

これは1対1の関係の例であり、この場合はuser address のエンティティ間の関係です。

以降のセクションで、これを実装する方法を見てみましょう。

3. 外部キーを使用する

3.1. 外部キーを使用したモデリング

外部キーベースの1対1マッピングを表す次のER diagramを見てみましょう。

An ER Diagram mapping Users to Addresses via an address_id foreign key

この例では、usersaddress_id 列はforeign keyからaddressです。

3.2. JPAで外部キーを使用して実装する

まず、User classを作成し、適切にアノテーションを付けましょう。

@Entity
@Table(name = "users")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private Long id;
    //...

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "address_id", referencedColumnName = "id")
    private Address address;

    // ... getters and setters
}

関連するエンティティフィールドのwe place the @OneToOne annotationAddressに注意してください。

また、we need to place the @JoinColumn annotationは、addressテーブルの主キーにマップされるusersテーブルの列の名前を構成します。 名前を指定しない場合、Hibernateはfollow some rulesでデフォルトの名前を選択します。

最後に、次のエンティティでは、@JoinColumn annotationを使用しないことに注意してください。 これは、外部キー関係のowning 側でのみ必要になるためです。 Simply put, whoever owns the foreign key column gets the @JoinColumn annotation.

Address entityは少し単純になります。

@Entity
@Table(name = "address")
public class Address {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private Long id;
    //...

    @OneToOne(mappedBy = "address")
    private User user;

    //... getters and setters
}

また、ここに@OneToOne annotationを配置する必要があります。 これはbidirectional relationshipであるためです。 The address side of the relationship is called the non-owning side. 

4. 共有主キーの使用

4.1. 共有主キーを使用したモデリング

この戦略では、新しい列を作成する代わりに、address_id, we’ll mark the primary key column (user_id) of the address table as the foreign key to the users table

An ER diagram with Users Tied to Addresses where they share the same primary key values

これらのエンティティは1対1の関係にあるという事実を利用して、ストレージスペースを最適化しました。

4.2. JPAで共有主キーを使用して実装する

定義が少しだけ変更されていることに注意してください。

@Entity
@Table(name = "users")
public class User {

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

    //...

    @OneToOne(mappedBy = "user", cascade = CascadeType.ALL)
    private Address address;

    //... getters and setters
}
@Entity
@Table(name = "address")
public class Address {

    @Id
    @Column(name = "id")
    private Long id;

    //...

    @OneToOne
    @MapsId
    private User user;

    //... getters and setters
}

@MapsId tells Hibernate to use the id column of address as both primary key and foreign key.また、the @Id column of the Address entity no longer uses the @GeneratedValue annotationに注意してください。

外部キーがaddress tableに存在するようになったため、mappedBy属性がUser classに移動されました。

5. 結合テーブルを使用する

1対1のマッピングには、OptionalMandatoryの2つのタイプがあります。 So far, we’ve seen only mandatory relationships.

ここで、従業員がワークステーションに関連付けられていると想像してみましょう。 1対1ですが、従業員がワークステーションを持っていない場合や、その逆の場合もあります。

5.1. 結合テーブルを使用したモデリング

 until now force us to put null values in the column to handle optional relationshipsについて説明した戦略。

通常、結合テーブルbut, using a join table, in this case, can help us to eliminate these null values:を検討するときは、many-to-many relationshipsについて考えます。

An ER diagram relating Employees to Workstations via a Join Table

これで、関係があるときはいつでも、emp_workstation tableにエントリを作成し、null を完全に回避します。

5.2. JPAで結合テーブルを使用して実装する

最初の例では@JoinColumn. を使用しました。今回は@JoinTableを使用します。

@Entity
@Table(name = "employee")
public class Employee {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private Long id;

    //...

    @OneToOne(cascade = CascadeType.ALL)
    @JoinTable(name = "emp_workstation",
      joinColumns =
        { @JoinColumn(name = "employee_id", referencedColumnName = "id") },
      inverseJoinColumns =
        { @JoinColumn(name = "workstation_id", referencedColumnName = "id") })
    private WorkStation workStation;

    //... getters and setters
}
@Entity
@Table(name = "workstation")
public class WorkStation {

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

    //...

    @OneToOne(mappedBy = "workStation")
    private Employee employee;

    //... getters and setters
}

@JoinTableは、関係を維持しながらテーブル結合戦略を採用するようにHibernateに指示します。

また、結合テーブルアノテーションを使用することを選択したため、Employee はこの関係の所有者です。

6. 結論

このチュートリアルでは、JPAとHibernateで1対1の関連付けを維持するさまざまな方法と、それぞれを使用するタイミングを学びました。

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