Добавление сущности в большое отношение "многие ко многим" в 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, хотя и позволяет добавлять в коллекции без необходимости запускать отношения, а также другие улучшения производительности. Это включено с плетением и доступно для типов коллекций, которые не поддерживают порядок.