1. 概要

Hibernateなどのオブジェクトリレーショナルマッピングツールを使用すると、データをオブジェクトに簡単に読み込むことができますが、複雑なデータモデルでクエリを作成するのが難しくなる可能性があります。

多対多の関係は常に困難ですが、関係自体のプロパティに基づいて関連エンティティを取得する場合はさらに困難になる可能性があります。

このチュートリアルでは、Hibernateの@WhereJoinTableアノテーションを使用してこの問題を解決する方法を見ていきます。

2. 基本的な@ManyToMany関係

単純な@ManyToMany関係から始めましょう。 ドメインモデルエンティティ、リレーションエンティティ、およびいくつかのサンプルテストデータが必要になります。

2.1. ドメインモデル

UserGroupの2つの単純なエンティティがあり、これらは @ManyToMany:として関連付けられていると想像してください。

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

    @Id
    @GeneratedValue
    private Long id;
    private String name;

    @ManyToMany
    private List<Group> groups = new ArrayList<>();

    // standard getters and setters

}
@Entity
public class Group {

    @Id
    @GeneratedValue
    private Long id;
    private String name;

    @ManyToMany(mappedBy = "groups")
    private List<User> users = new ArrayList<>();

    // standard getters and setters

}

ご覧のとおり、Userエンティティは複数のGroupエンティティのメンバーになることができます。 同様に、Groupエンティティには複数のUserエンティティを含めることができます。

2.2. 関係エンティティ

@ManyToManyアソシエーションの場合、リレーションテーブルと呼ばれる個別のデータベーステーブルが必要です。リレーションテーブルには、少なくとも2つの列が含まれている必要があります。関連するユーザーの主キーGroupエンティティ。

2つの主キー列のみで、Hibernateマッピングはこのリレーションテーブルを表すことができます。

ただし、リレーションテーブルに追加のデータを配置する必要がある場合は、多対多リレーション自体のリレーションエンティティも定義する必要があります。

これを行うためにUserGroupRelationクラスを作成しましょう。

@Entity(name = "r_user_group")
public class UserGroupRelation implements Serializable {

    @Id
    @Column(name = "user_id", insertable = false, updatable = false)
    private Long userId;

    @Id
    @Column(name = "group_id", insertable = false, updatable = false)
    private Long groupId;

}

ここでは、後で参照できるように、エンティティにr_user_groupという名前を付けました。

追加のデータとして、各グループのすべてのユーザーの役割を保存するとします。 したがって、UserGroupRole列挙を作成します。

public enum UserGroupRole {
    MEMBER, MODERATOR
}

次に、 roleプロパティをUserGroupRelation:に追加します

@Enumerated(EnumType.STRING)
private UserGroupRole role;

最後に、正しく構成するには、Usergroupsコレクションに@JoinTableアノテーションを追加する必要があります。 ここでは、 r_user_group、 UserGroupRelation:のエンティティ名を使用して結合テーブル名を指定します。

@ManyToMany
@JoinTable(
    name = "r_user_group",
    joinColumns = @JoinColumn(name = "user_id"),
    inverseJoinColumns = @JoinColumn(name = "group_id")
)
private List<Group> groups = new ArrayList<>();

2.3. サンプルデータ

統合テストのために、いくつかのサンプルデータを定義しましょう。

public void setUp() {
    session = sessionFactory.openSession();
    session.beginTransaction();
    
    user1 = new User("user1");
    user2 = new User("user2");
    user3 = new User("user3");

    group1 = new Group("group1");
    group2 = new Group("group2");

    session.save(group1);
    session.save(group2);

    session.save(user1);
    session.save(user2);
    session.save(user3);

    saveRelation(user1, group1, UserGroupRole.MODERATOR);
    saveRelation(user2, group1, UserGroupRole.MODERATOR);
    saveRelation(user3, group1, UserGroupRole.MEMBER);

    saveRelation(user1, group2, UserGroupRole.MEMBER);
    saveRelation(user2, group2, UserGroupRole.MODERATOR);
}

private void saveRelation(User user, Group group, UserGroupRole role) {

    UserGroupRelation relation = new UserGroupRelation(user.getId(), group.getId(), role);
    
    session.save(relation);
    session.flush();
    session.refresh(user);
    session.refresh(group);
}

ご覧のとおり、user1user2は2つのグループに属しています。 また、user1group1ではMODERATORであると同時に、ではMEMBERの役割を果たしていることに注意してください。 group2

3. @ManyToMany関係の取得

エンティティを適切に構成したので、Userエンティティのグループをフェッチしましょう。

3.1. シンプルフェッチ

グループをフェッチするには、アクティブなHibernateセッション内で UsergetGroups()メソッドを呼び出すだけです。

List<Group> groups = user1.getGroups();

グループの出力は次のようになります。

[Group [name=group1], Group [name=group2]]    

しかし、グループの役割が MODERATORのみであるユーザーのグループを取得するにはどうすればよいですか?

3.2. リレーションエンティティのカスタムフィルター

@WhereJoinTableアノテーションを使用して、フィルタリングされたグループのみを直接取得できます。

新しいプロパティをmoderatorGroupsとして定義し、@WhereJoinTableアノテーションを付けましょう。 このプロパティを介して関連エンティティにアクセスすると、ユーザーがMODERATOR。であるグループのみが含まれます。

MODERATOR ロールでグループをフィルタリングするには、SQLwhere句を追加する必要があります。

@WhereJoinTable(clause = "role='MODERATOR'")
@ManyToMany
@JoinTable(
    name = "r_user_group",
    joinColumns = @JoinColumn(name = "user_id"),
    inverseJoinColumns = @JoinColumn(name = "group_id")
)
private List<Group> moderatorGroups = new ArrayList<>();

したがって、指定されたSQLwhere句が適用されたグループを簡単に取得できます。

List<Group> groups = user1.getModeratorGroups();

出力は、ユーザーが MODERATOR:の役割のみを持つグループになります。

[Group [name=group1]]

4. 結論

このチュートリアルでは、Hibernateの@WhereJoinTableアノテーションを使用して、リレーションテーブルのプロパティに基づいて@ManyToManyコレクションをフィルタリングする方法を学習しました。

いつものように、このチュートリアルで提供されているすべてのコードサンプルは、GitHubから入手できます。