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

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

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

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

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

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

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

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



답변

  • 관계의 소유권은 ‘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()


답변

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

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

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


답변

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

@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에 문제를 일으킬 수 있습니까? 어떤 유형의 문제입니까?

감사!

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


답변

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)에서 지원됩니다.


답변

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


답변

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

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


답변

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

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

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