JPA (및 해당 조인 테이블 행)에서 ManyToMany 관계가있는 엔티티를 제거하는 방법은 무엇입니까?


91

그룹과 사용자라는 두 개의 엔티티가 있다고 가정 해 보겠습니다. 모든 사용자는 여러 그룹의 구성원이 될 수 있으며 모든 그룹은 여러 사용자를 가질 수 있습니다.

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

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

이제 그룹을 제거하고 싶습니다 (구성원이 많다고 가정 해 보겠습니다).

문제는 일부 그룹에서 EntityManager.remove ()를 호출 할 때 JPA 공급자 (내 경우에는 Hibernate) 가 조인 테이블에서 행을 제거하지 않고 외래 키 제한으로 인해 삭제 작업이 실패한다는 것입니다. 사용자에 대한 remove () 호출은 잘 작동합니다 (나는 이것이 관계의 소유와 관련이 있다고 생각합니다).

그렇다면이 경우 어떻게 그룹을 제거 할 수 있습니까?

내가 생각 해낼 수있는 유일한 방법은 그룹의 모든 사용자를로드 한 다음 모든 사용자에 대해 그룹에서 현재 그룹을 제거하고 사용자를 업데이트하는 것입니다. 그러나이 그룹을 삭제할 수 있도록 그룹의 모든 사용자에 대해 update ()를 호출하는 것은 우스꽝스러운 것 같습니다.

답변:


86
  • 관계의 소유권은 'mappedBy'속성을 주석에 배치하는 위치에 따라 결정됩니다. 'mappedBy'를 넣은 엔티티는 소유자가 아닙니다. 양측이 모두 소유자가 될 기회는 없습니다. '사용자 삭제'사용 사례가없는 경우 Group현재 User소유자이므로 소유권을 엔티티 로 간단히 이동할 수 있습니다.
  • 반면에, 당신은 그것에 대해 묻지 않았지만 알아야 할 가치가 있습니다. groups와는 users서로 결합되지 않습니다. 내 말은, Group1.users에서 User1 인스턴스를 삭제 한 후 User1.groups 컬렉션이 자동으로 변경되지 않습니다.
  • 대체로 누가 소유자인지 결정하는 것이 좋습니다. User주인이 있다고합시다 . 그런 다음 사용자를 삭제할 때 관련 사용자 그룹이 자동으로 업데이트됩니다. 그러나 그룹을 삭제할 때는 다음과 같이 관계를 삭제해야합니다.

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

Thnx! 나는 같은 문제가 있었고 당신의 해결책이 그것을 해결했습니다. 하지만이 문제를 해결할 다른 방법이 있는지 알아야합니다. 그것은 끔찍한 코드를 생성합니다. em.remove (entity) 일 수없는 이유는 무엇 입니까?
Royi Freifeld 2013

2
이것은이면에서 최적화됩니까? 전체 데이터 세트를 쿼리하고 싶지 않으니까.
Ced

1
이것은 정말 나쁜 접근 방식입니다. 만약 그 그룹에 수천 명의 사용자가 있다면 어떨까요?
Sergiy Sokolenko 2007 년

2
이것은 관계 테이블을 업데이트하지 않으며 관계 테이블에서이 관계에 대한 행은 여전히 ​​남아 있습니다. 나는 테스트하지 않았지만 잠재적으로 문제를 일으킬 수 있습니다.
Saygın Doğu

@SergiySokolenko for 루프에서 데이터베이스 쿼리가 실행되지 않습니다. 이들 모두는 Transactional 함수가 남은 후에 일괄 처리됩니다.
Kilves

42

다음은 나를 위해 작동합니다. 관계 (그룹)의 소유자가 아닌 엔터티에 다음 메서드를 추가합니다.

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

이것이 작동하려면 그룹에 업데이트 된 사용자 목록이 있어야합니다 (자동으로 수행되지 않음). 따라서 사용자 엔터티의 그룹 목록에 그룹을 추가 할 때마다 그룹 엔터티의 사용자 목록에도 사용자를 추가해야합니다.


1
이 솔루션을 사용하려면 cascade = CascadeType.ALL을 설정하면 안됩니다. 그렇지 않으면 완벽하게 작동합니다!
ltsstar

@damian 안녕하세요, 귀하의 솔루션을 사용했지만 현재 오류가 발생했습니다. 난 사용자에게 하나 개 이상의 그룹을 추가하면 내가이 예외 ... 얻을 수 있기 때문에
아드 난 압둘 칼리 크

@Damian 이것이 작동하려면 그룹에 업데이트 된 사용자 목록이 있어야합니다 (자동으로 수행되지 않음). 따라서 사용자 엔터티의 그룹 목록에 그룹을 추가 할 때마다 그룹 엔터티의 사용자 목록에도 사용자를 추가해야합니다. 이에 정교한 시겠어요,,, 나에게 thnks 예를 들어 줄
아드 난 압둘 칼리 크

27

가능한 해결책을 찾았지만 ... 좋은 해결책인지 모르겠습니다.

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

나는 이것을 시도했고 작동합니다. 역할을 삭제하면 관계도 삭제되고 (권한 엔터티 제외) 권한을 삭제하면 역할과의 관계도 삭제됩니다 (역할 인스턴스는 제외되지 않음). 그러나 우리는 단방향 관계를 두 번 매핑하고 두 엔티티가 관계의 소유자입니다. 이것이 Hibernate에 문제를 일으킬 수 있습니까? 어떤 유형의 문제입니까?

감사!

위의 코드는 다른 게시물 과 관련된 것입니다.


컬렉션 새로 고침 문제?
jelies 2010-08-26

12
이것은 정확하지 않습니다. 양방향 ManyToMany에는 관계의 소유자 측이 하나만 있어야합니다. 이 솔루션은 양쪽을 모두 소유자로 만들고 결국 중복 레코드가 생성됩니다. 중복 레코드를 방지하려면 목록 대신 세트를 사용해야합니다. 그러나 Set를 사용하는 것은 권장되지 않는 작업을 수행하기위한 해결 방법 일뿐입니다.
L. Holanda 2015

4
그런 일반적인 시나리오에 대한 모범 사례는 무엇입니까?
SalutonMondo

8

JPA / Hibernate 솔루션의 대안으로 : (Oracle 구문)과 같이 조인 테이블에있는 foregin 키의 데이터베이스 정의에 CASCADE DELETE 절을 사용할 수 있습니다.

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

이렇게하면 그룹을 삭제할 때 DBMS 자체가 그룹을 가리키는 행을 자동으로 삭제합니다. 그리고 삭제가 Hibernate / JPA, JDBC, DB에서 수동으로 이루어 지든 다른 방법 으로든 작동합니다.

계단식 삭제 기능은 모든 주요 DBMS (Oracle, MySQL, SQL Server, PostgreSQL)에서 지원됩니다.


3

그 가치가 무엇인지 EclipseLink 2.3.2.v20111125-r10461을 사용하고 있으며 @ManyToMany 단방향 관계가 있으면 설명하는 문제를 관찰합니다. 그러나 양방향 @ManyToMany 관계로 변경하면 비 소유 측에서 엔티티를 삭제할 수 있으며 JOIN 테이블이 적절하게 업데이트됩니다. 이것은 모든 캐스케이드 속성을 사용하지 않고 있습니다.


2

이것은 나를 위해 작동합니다.

@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) 메소드를 표시하면 한 세션에서 전체 프로세스를 수행하고 시간을 절약 할 수 있습니다.


1

이것은 좋은 해결책입니다. 가장 좋은 부분은 SQL 측에 있습니다. 모든 레벨로의 미세 조정이 쉽습니다.

MySql 및 MySql Workbench를 사용하여 필수 외래 키를 삭제할 때 계단식으로 배열했습니다.

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

SO에 오신 것을 환영합니다. 잠시 시간을내어 형식화 및 교정 읽기를 개선하기 위해이 내용을 살펴보십시오. stackoverflow.com/help/how-to-ask
petezurich

1

이것이 내가 한 일입니다. 누군가가 유용하다고 생각할 수 있기를 바랍니다.

@Transactional
public void deleteGroup(Long groupId) {
    Group group = groupRepository.findById(groupId).orElseThrow();
    group.getUsers().forEach(u -> u.getGroups().remove(group));
    userRepository.saveAll(group.getUsers());
    groupRepository.delete(group);
}

0

제 경우에는 다음과 같이 mappingBy와 조인 된 테이블을 삭제했습니다.

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

3
다 대다에서 cascadeType.all을 사용하지 마십시오. 삭제시 A 레코드는 연관된 모든 B 레코드를 삭제합니다. 그리고 B 레코드는 관련된 모든 A 레코드를 삭제합니다. 등등. 큰 안돼.
Josiah 19

0

이것은 참조로 인해 사용자를 삭제하지 못한 유사한 문제에서 저에게 효과적입니다. 감사합니다

@ManyToMany(cascade = {CascadeType.MERGE, CascadeType.PERSIST,CascadeType.REFRESH})
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.