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


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

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

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

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

проблема в том, что когда я вызываю EntityManager.удалите () в какой-то группе, провайдер JPA (в моем случае Hibernate) не удаляет строки из таблицы и операция удаления не выполняется из-за ограничений внешнего ключа. Вызов remove () on User работает нормально (I думаю, это как-то связано с владением стороной отношений).

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

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

7 71

7 ответов:

  • принадлежность отношения определяется тем, где вы помещаете атрибут 'mappedBy' в аннотацию. Сущность, которую вы ставите "mappedBy", - это тот, который не является владельцем. Нет никаких шансов для обеих сторон, чтобы быть хозяевами. Если у вас нет варианта использования "удалить пользователя", вы можете просто переместить право собственности на Group сущность, так как в настоящее время User является собственником.
  • С другой стороны, вы не спрашивали об этом, но одна вещь, которую стоит знать. Элемент groups и users не сочетаются друг с другом. Я имею в виду, после удаления экземпляра User1 из Group1.пользователей, пользователя user1.группы коллекций не изменяется автоматически (что довольно удивительно для меня),
  • в целом, я бы предложил вам решить, кто является владельцем. Пусть говорят User является собственником. Затем при удалении пользователя отношение пользователь-группа будет обновляться автоматически. Но при удалении группы вы должны позаботиться об удалении отношении себя это:

entityManager.remove(group)
for (User user : group.users) {
     user.groups.remove(group);
}
...
// then merge() and flush()

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

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

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

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

@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;
    }

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

спасибо!

приведенный выше код-это из другой post связанные.

в качестве альтернативы решениям JPA/Hibernate : вы можете использовать предложение 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).

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

Это хорошее решение. Лучшая часть находится на стороне SQL-точная настройка на любой уровень проста.

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

ALTER TABLE schema.joined_table 
ADD CONSTRAINT UniqueKey
FOREIGN KEY (key2)
REFERENCES schema.table1 (id)
ON DELETE CASCADE;

это работает для меня:

@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.торговая операция.аннотация.Транзакционный), это будет делать весь процесс за один сеанс, экономит некоторое время.