0Pricing
Java Academy · レッスン

1対多と多対多のリレーションシップ

カスケードタイプとフェッチ戦略を指定して、@OneToMany、@ManyToMany、@JoinTableをマッピングします。

「1対多と多対多のリレーションシップ」はCoddyKit上の無料Java Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはJava Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Java Academyコースには全4レッスンが含まれています。

JPAの関連付けの種類

JPAは、@OneToOne、@OneToMany、@ManyToOne、@ManyToManyの4種類の関連付けをサポートしています。それぞれが、外部キーまたは結合テーブルの制約にマッピングされます。

@ManyToOne: 所有側

多対一の関連(例: 複数の Order が 1 つの User に属する場合)は、@ManyToOne と外部キー用の @JoinColumn でマッピングします。

@Entity
public class Order {
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id", nullable = false)
    private User user;
}

@OneToMany: 逆側

逆側(User が複数の Order を持つ場合)では、@OneToMany(mappedBy = "user") を使用します。mappedBy 属性は、外部キーを保持する所有側のフィールドを指します。

@Entity
public class User {
    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Order> orders = new ArrayList<>();
}

双方向関連の整合性を保つヘルパー

双方向関連では、フラッシュする前にオブジェクトグラフの整合性を保てるよう、ヘルパーメソッドで関連の両側を更新してください。

public void addOrder(Order order) {
    orders.add(order);
    order.setUser(this); // keep both sides in sync
}
public void removeOrder(Order order) {
    orders.remove(order);
    order.setUser(null);
}

カスケードタイプ

CascadeType.ALL は、親から子へすべての操作(persist、merge、remove、refresh、detach)を伝播します。一般的に安全な選択肢は PERSIST と MERGE です。REMOVE を使用すると、親の削除時に子も削除されます。

@OneToMany(mappedBy = "user",
           cascade = {CascadeType.PERSIST, CascadeType.MERGE},
           orphanRemoval = true)
private List<Order> orders;

orphanRemoval: 子エンティティの自動削除

orphanRemoval = true を設定すると、明示的に entityManager.remove() を呼び出さなくても、親のコレクションから子エンティティを削除したときに、その子エンティティが削除されます。

user.getOrders().remove(order); // triggers DELETE for the orphaned order on flush

@ManyToMany と @JoinTable

多対多の関連(例: Student と Course)では、中間テーブルを使用します。所有側で @JoinTable を使って定義し、逆側では mappedBy を使用します。

@Entity
public class Student {
    @ManyToMany
    @JoinTable(
        name = "student_course",
        joinColumns = @JoinColumn(name = "student_id"),
        inverseJoinColumns = @JoinColumn(name = "course_id"))
    private Set<Course> courses = new HashSet<>();
}

中間テーブルの追加カラム

中間テーブルに追加カラム(例: 受講登録日)が必要な場合は、@ManyToMany を 2 つの @OneToMany / @ManyToOne 関連に置き換え、中間テーブルを表す新しいエンティティを作成します。

@Entity
public class Enrollment {
    @ManyToOne Student student;
    @ManyToOne Course course;
    private LocalDate enrolledAt;
}

FetchType: LAZY と EAGER

デフォルトのフェッチ設定では、@ManyToOne と @OneToOne は EAGER(即座に読み込み)で、@OneToMany と @ManyToMany は LAZY(アクセス時に読み込み)です。N+1 クエリを避けるため、コレクションには常に LAZY を使用してください。

// Always override to LAZY:
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "category_id")
private Category category;

N+1 クエリ問題

ループ内で遅延読み込みのコレクションにアクセスすると、エンティティごとに 1 回ずつクエリが実行され、N+1 問題が発生します。JPQL の JOIN FETCH または @EntityGraph を使って、コレクションを 1 回のクエリで読み込んで解決してください。

// N+1 problem:
List<User> users = repo.findAll();
users.forEach(u -> u.getOrders().size()); // 1 + N queries!
// Fix:
@Query("SELECT u FROM User u LEFT JOIN FETCH u.orders")
List<User> findAllWithOrders();

コレクションの Set と List

@ManyToMany には Set を使用してください(中間テーブルの重複行を避けられます)。順序が重要な場合は @OneToMany に List を使用します。@ManyToMany で List を使用してはいけません。Hibernate がすべての行を削除して再挿入するためです。

確認問題

@OneToMany アノテーションの mappedBy は何を示しますか?

まとめ

所有側(外部キー側)には @ManyToOne と @JoinColumn を使用します。逆側には @OneToMany(mappedBy=...) を使用します。コレクションは常に LAZY にしてください。N+1 問題は JOIN FETCH で解決します。追加カラムがある多対多の関連には、中間エンティティを使用します。

よくある質問

「1対多と多対多のリレーションシップ」レッスンは無料ですか?

はい。「1対多と多対多のリレーションシップ」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Java Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Java Academyコースには全4レッスンが含まれています。

「1対多と多対多のリレーションシップ」で何を学びますか?

カスケードタイプとフェッチ戦略を指定して、@OneToMany、@ManyToMany、@JoinTableをマッピングします。 ブラウザで直接実行するハンズオンコードでJava Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Java Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのJava Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。

「1対多と多対多のリレーションシップ」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このJava Academyレッスンでコードを書いて実行できますか?

はい。すべてのJava Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. JPAアノテーションによるエンティティマッピング
  2. Spring Dataリポジトリとクエリメソッド
  3. 1対多と多対多のリレーションシップ
  4. ページネーション、ソート、プロジェクション
← Java Academyに戻る