08 апр. 2013 г., 21:16 отrdk

Как удалить сущность с отношением ManyToMany в JPA (и соответствующие строки таблицы соединения)?

Допустим, у меня есть две сущности: группа и пользователь. Каждый пользователь может быть членом многих групп, и каждая группа может иметь много пользователей.

@Entity
public class User {
    @ManyToMany
    Set<Group> groups;
    //...
}

@Entity
public class Group {
    @ManyToMany(mappedBy="groups")
    Set<User> users;
    //...
}

Теперь я хочу удалить группу (скажем, в ней много участников).

Проблема в том, что когда я вызываю EntityManager.remove () в какой-либо группе, JPA-провайдере (в моем случае Hibernate)does not remove rows from join table и операция удаления завершается неудачно из-за ограничений внешнего ключа. Вызов метода remove () для пользователя работает нормально (я полагаю, это как-то связано с собственной стороной отношений).

Так как же я могу удалить группу в этом случае?

Единственный способ, которым я мог придумать, - это загрузить всех пользователей в группе, затем для каждого пользователя удалить текущую группу из своих групп и обновить пользователя. Но мне кажется смешным вызывать update () для каждого пользователя из группы, чтобы иметь возможность удалить эту группу.

Ответы на вопрос(8)

10 янв. 2019 г., 01:32 отszachMati

м:

@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name = "user_group", joinColumns = {
        @JoinColumn(name = "user", referencedColumnName = "user_id")
}, inverseJoinColumns = {
        @JoinColumn(name = "group", referencedColumnName = "group_id")
})
private List<User> users;

@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JsonIgnore
private List<Group> groups;
09 июл. 2017 г., 04:18 отpetezurichuser1776955

Тонкая настройка на любой уровень проста.

Я использовал MySql и MySql Workbench для каскадирования при удалении для требуемого внешнего ключа.

ALTER TABLE schema.joined_table 
ADD CONSTRAINT UniqueKey
FOREIGN KEY (key2)
REFERENCES schema.table1 (id)
ON DELETE CASCADE;
29 июн. 2015 г., 11:50 отPierre Henry

вы можете использовать предложение CASCADE DELETE в определении базы данных вашего ключа foregin в таблице соединений, например (синтаксис Oracle):

CONSTRAINT fk_to_group
     FOREIGN KEY (group_id)
     REFERENCES group (id)
     ON DELETE CASCADE

Таким образом, сама СУБД автоматически удаляет строку, которая указывает на группу, при удалении группы. И это работает независимо от того, производится ли удаление из Hibernate / JPA, JDBC, вручную в БД или любым другим способом.

возможность каскадного удаления поддерживается всеми основными СУБД (Oracle, MySQL, SQL Server, PostgreSQL).

02 июн. 2012 г., 05:21 отNBW

я использую EclipseLink 2.3.2.v20111125-r10461, и если у меня есть однонаправленное отношение @ManyToMany, я наблюдаю проблему, которую вы описываете. Однако, если я изменю его на двустороннее отношение @ManyToMany, я смогу удалить сущность со стороны, не являющейся владельцем, и таблица JOIN будет обновлена соответствующим образом. Это все без использования каких-либо каскадных атрибутов.

23 мая 2017 г., 14:10 отCommunityjelies

но ... я не знаю, является ли это хорошим решением.

@Entity
public class Role extends Identifiable {

    @ManyToMany(cascade ={CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
    @JoinTable(name="Role_Permission",
            joinColumns=@JoinColumn(name="Role_id"),
            inverseJoinColumns=@JoinColumn(name="Permission_id")
        )
    public List<Permission> getPermissions() {
        return permissions;
    }

    public void setPermissions(List<Permission> permissions) {
        this.permissions = permissions;
    }
}

@Entity
public class Permission extends Identifiable {

    @ManyToMany(cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
    @JoinTable(name="Role_Permission",
            joinColumns=@JoinColumn(name="Permission_id"),
            inverseJoinColumns=@JoinColumn(name="Role_id")
        )
    public List<Role> getRoles() {
        return roles;
    }

    public void setRoles(List<Role> roles) {
        this.roles = roles;
    }

Я попробовал это, и это работает. При удалении роли также удаляются отношения (но не объекты прав доступа), а при удалении разрешения также удаляются отношения с ролью (но не экземпляр роли). Но мы наносим на карту однонаправленное отношение два раза, и оба объекта являются его владельцами. Может ли это вызвать некоторые проблемы в спящем режиме? Какие проблемы?

Спасибо!

Код выше от другогосообщение связанные с.

16 февр. 2013 г., 16:30 отdamian

который не является владельцем отношения (группа)

@PreRemove
private void removeGroupsFromUsers() {
    for (User u : users) {
        u.getGroups().remove(this);
    }
}

Имейте в виду, что для этого у группы должен быть обновленный список пользователей (который не выполняется автоматически). поэтому каждый раз, когда вы добавляете группу в список групп в сущности пользователя, вы также должны добавлять пользователя в список пользователей в сущности группы.

06 июл. 2009 г., 22:15 отGrzegorz Oledzki
The ownership of the relation is determined by where you place the 'mappedBy' attribute to the annotation. The entity you put 'mappedBy' is the one which is NOT the owner. There's no chance for both sides to be owners. If you don't have a 'delete user' use-case you could simply move the ownership to the Group entity, as currently the User is the owner. On the other hand, you haven't been asking about it, but one thing worth to know. The groups and users are not combined with each other. I mean, after deleting User1 instance from Group1.users, the User1.groups collections is not changed automatically (which is quite surprising for me), All in all, I would suggest you decide who is the owner. Let say the User is the owner. Then when deleting a user the relation user-group will be updated automatically. But when deleting a group you have to take care of deleting the relation yourself like this:
entityManager.remove(group)
for (User user : group.users) {
     user.groups.remove(group);
}
...
// then merge() and flush()
06 мар. 2018 г., 22:16 отMehul Katpara

@Transactional
public void remove(Integer groupId) {
    Group group = groupRepository.findOne(groupId);
    group.getUsers().removeAll(group.getUsers());

    // Other business logic

    groupRepository.delete(group);
}

Также отметьте метод @Transactional (org.springframework.transaction.annotation.Transactional), это сделает весь процесс за один сеанс, сэкономит время.

ВАШ ОТВЕТ НА ВОПРОС