Добавление сущности в большое отношение "многие ко многим" в JPA
У меня есть сущность группы, которая имеет список пользовательских сущностей во многих отношениях. Он сопоставляется типичной таблицей соединений, содержащей два идентификатора. Этот список может быть очень большим, миллион или больше пользователей в группе.
Мне нужно добавить нового пользователя в группу, как правило, что будет что-то вроде
group.getUsers().add(user);
user.getGroups().add(group);
em.merge(group);
em.merge(user);
Если я понимаю типичную операцию JPA, потребуется ли для этого вытащить весь список из 1 миллиона+ пользователей в коллекцию, чтобы добавить новый пользователь, а затем сохранить? По-моему, это не очень масштабируемо.
должен ли я просто не определять эту связь в JPA? Должен ли я манипулировать записями таблицы соединений непосредственно в таком случае?
пожалуйста, простите свободный синтаксис, я на самом деле использую Spring Data JPA, поэтому я не использую диспетчер сущностей напрямую очень часто, но вопрос, похоже, общий для JPA, поэтому я хотел бы поставить его таким образом.
3 ответов
дизайн ваших моделей, как это и играть с UserGroup для ассоциаций.
@Entity
public class User {
@OneToMany(cascade = CascadeType.ALL, mappedBy = "user",fetch = FetchType.LAZY)
@OnDelete(action = OnDeleteAction.CASCADE)
private Set<UserGroup> userGroups = new HashSet<UserGroup>();
}
@Entity
@Table(name="user_group",
uniqueConstraints = {@UniqueConstraint(columnNames = {"user_id", "group_id"})})
public class UserGroup {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
@ForeignKey(name = "usergroup_user_fkey")
private User user;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "group_id", nullable = false)
@ForeignKey(name = "usergroup_group_fkey")
private Group group;
}
@Entity
public class Group {
@OneToMany(cascade = CascadeType.ALL, mappedBy="group", fetch = FetchType.LAZY )
@OnDelete(action = OnDeleteAction.CASCADE)
private Set<UserGroup> userGroups = new HashSet<UserGroup>();
}
сделайте так.
User user = findUserId(id); //All groups wont be loaded they are marked lazy
Group group = findGroupId(id); //All users wont be loaded they are marked lazy
UserGroup userGroup = new UserGroup();
userGroup.setUser(user);
userGroup.setGroup(group);
em.save(userGroup);
классы коллекций, возвращаемых getUsers()
и getGroups()
Не обязательно иметь их содержимое в памяти, и если у вас включена ленивая выборка, как я предполагаю, что вы делаете для таких больших отношений, поставщик сохраняемости должен быть достаточно умен, чтобы признать, что вы не пытаетесь прочитать содержимое, а просто добавляете значение. (Аналогично, вызов size()
в коллекции обычно вызывает SQL COUNT
запрос, а не фактическая загрузка и подсчет элементов.)
использование сопоставления ManyToMany эффективно кэширует коллекцию в сущности, поэтому вы можете не захотеть делать это для больших коллекций, так как отображение ее или передача сущности с ней будет убивать производительность.
вместо этого вы можете удалить сопоставление с обеих сторон и создать сущность для таблицы отношений, которую можно использовать в запросах, когда вам нужно получить доступ к связи. Использование промежуточного объекта позволит вам использовать пейджинг и курсоры, поэтому что вы можете ограничить данные, которые могут быть возвращены в пригодные для использования куски, и вы можете вставить новый объект для представления новых отношений с легкостью.
отслеживание изменений атрибутов EclipseLink, хотя и позволяет добавлять в коллекции без необходимости запускать отношения, а также другие улучшения производительности. Это включено с плетением и доступно для типов коллекций, которые не поддерживают порядок.