Перейти к основному содержимому

Hibernate @WhereJoinTable Аннотация

· 4 мин. чтения

1. Обзор

Использование инструмента объектно-реляционного сопоставления, такого как Hibernate, упрощает чтение наших данных в объекты, но может затруднить формирование наших запросов со сложными моделями данных.

Отношения «многие ко многим» всегда сложны, но они могут быть более сложными, когда мы хотим получить связанные объекты на основе некоторого свойства самого отношения.

В этом уроке мы рассмотрим, как решить эту проблему, используя аннотацию Hibernate @WhereJoinTable .

2. Базовое отношение @ManyToMany

Начнем с простого отношения @ManyToMany . Нам потребуются объекты модели предметной области, объект отношения и некоторые образцы тестовых данных.

2.1. Модель домена

Давайте представим, что у нас есть два простых объекта, User и Group , которые связаны как @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

}

Как мы видим, наша сущность « Пользователь » может быть членом более чем одной сущности « Группа» . Точно так же объект «Группа» может содержать более одного объекта « Пользователь ».

2.2. Объект отношения

Для ассоциаций @ManyToMany нам нужна отдельная таблица базы данных, называемая таблицей отношений. Таблица отношений должна содержать по крайней мере два столбца: Первичные ключи связанных сущностей пользователя и группы .

Только с двумя столбцами первичного ключа наше сопоставление 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
}

Далее мы добавим свойство роли в UserGroupRelation:

@Enumerated(EnumType.STRING)
private UserGroupRole role;

Наконец, чтобы настроить его правильно, нам нужно добавить аннотацию @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);
}

Как мы видим, user1 и user2 находятся в двух группах. Кроме того, мы должны заметить, что хотя user1 является МОДЕРАТОРОМ в группе1 , в то же время он имеет роль УЧАСТНИК в группе2 .

3. Получение отношений @ManyToMany

Теперь, когда мы правильно настроили наши объекты, давайте получим группы объекта User .

3.1. Простая выборка

Чтобы получить группы, мы можем просто вызвать метод getGroups () пользователя внутри активного сеанса Hibernate:

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

Наш вывод для групп будет:

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

Но как мы можем получить группы пользователя, чья групповая роль — только МОДЕРАТОР?

3.2. Пользовательские фильтры для сущности отношения

Мы можем использовать аннотацию @WhereJoinTable для прямого получения только отфильтрованных групп.

Давайте определим новое свойство как moderatorGroups и поместим на него аннотацию @WhereJoinTable . Когда мы получаем доступ к связанным объектам через это свойство, оно будет содержать только те группы, МОДЕРАТОРОМ которых является наш пользователь .

Нам нужно добавить предложение SQL where для фильтрации групп по роли МОДЕРАТОРА :

@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<>();

Таким образом, мы можем легко получить группы с указанным оператором SQL where:

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

Нашим выходом будут группы, в которых пользователь имеет только роль МОДЕРАТОРА:

[Group [name=group1]]

4. Вывод

В этом руководстве мы узнали, как фильтровать коллекции @ManyToMany на основе свойства таблицы отношений с помощью аннотации Hibernate @WhereJoinTable .

Как всегда, все примеры кода, приведенные в этом руководстве, доступны на GitHub .