JPA : 단방향 다 대일 및 계단식 삭제


95

다음과 같은 단방향 @ManyToOne 관계 가 있다고 가정 합니다.

@Entity
public class Parent implements Serializable {

    @Id
    @GeneratedValue
    private long id;
}

@Entity
public class Child implements Serializable {

    @Id
    @GeneratedValue
    private long id;

    @ManyToOne
    @JoinColumn
    private Parent parent;  
}

나는 부모 P와 자녀가있는 경우 C 1 ... C N P 다시 참조, 자동으로 아이를 제거하는 JPA에서 깨끗하고 예쁜 방법이 C 1 ... C N P가 제거 될 때 (즉 entityManager.remove(P))?

내가 찾고있는 것은 ON DELETE CASCADESQL 과 비슷한 기능 입니다.


1
'Child'에만 'Parent'에 대한 참조가 있더라도 (이런 식으로 참조는 단방향 임) '@OneToMany'매핑 및 'Cascade = ALL'속성이있는 'Child'목록을 다음에 추가하는 것은 문제가됩니다. '부모'? 나는 JPA가 힘든 한 쪽만이 참조를 보유하고 있다는 것을 해결해야한다고 가정합니다.
kvDennis

1
@kvDennis,다면을 한쪽에 단단히 연결하고 싶지 않은 경우가 있습니다. 예 : 보안 권한이 투명한 "추가 기능"인 ACL과 유사한 설정
Bachi

답변:


73

JPA의 관계는 부모를 자식과 양방향으로 연결하지 않는 한 항상 단방향입니다. 부모에서 자식으로의 계단식 REMOVE 작업에는 부모에서 자식으로의 관계가 필요합니다 (반대가 아닌).

따라서 다음을 수행해야합니다.

  • 단방향 @ManyToOne관계를 양방향 @ManyToOne또는 단방향 으로 변경하십시오 @OneToMany. 그런 다음 REMOVE 연산을 계단식으로 배열 EntityManager.remove하여 부모와 자식을 제거 할 수 있습니다 . 또한 orphanRemovaltrue로 지정 하여 부모 컬렉션의 자식 엔터티가 null로 설정된 경우 고아 자식을 삭제할 수 있습니다 . 즉, 부모 컬렉션에없는 자식을 제거합니다.
  • 또는 하위 테이블의 외래 키 제약 조건을 ON DELETE CASCADE. 지속성 컨텍스트를 새로 고쳐야 하므로 호출 EntityManager.clear()후 호출 EntityManager.remove(parent)해야합니다. 하위 엔티티는 데이터베이스에서 삭제 된 후 지속성 컨텍스트에 존재하지 않아야합니다.

7
JPA 주석으로 No2를 수행하는 방법이 있습니까?
user2573153

3
Hibernate xml 매핑으로 No2를 어떻게합니까?
arg20 2014 년

92

최대 절전 모드를 JPA 공급자로 사용하는 경우 @OnDelete 주석을 사용할 수 있습니다. 이 주석은 데이터베이스에 자식 삭제를 위임하는 ON DELETE CASCADE 트리거를 관계에 추가 합니다.

예:

public class Parent {

        @Id
        private long id;

}


public class Child {

        @Id
        private long id;

        @ManyToOne
        @OnDelete(action = OnDeleteAction.CASCADE)
        private Parent parent;
}

이 솔루션을 사용하면 자식에서 부모로의 단방향 관계만으로도 모든 자식을 자동으로 제거 할 수 있습니다. 이 솔루션에는 리스너 등이 필요하지 않습니다. 또한 DELETE FROM Parent WHERE id = 1 과 같은 쿼리 는 자식을 제거합니다.


4
이런 식으로 작동하도록 만들 수 없습니다. 특정 버전의 최대 절전 모드 또는 이와 같은 다른 자세한 예제가 있습니까?
Mardari

3
왜 그것이 당신을 위해 작동하지 않는지 말하기는 어렵습니다. 이 작업을 수행하려면 스키마를 다시 생성하거나 계단식 삭제를 수동으로 추가해야 할 수 있습니다. @OnDelete 주석은 버전이 문제라고 생각하지 않기 때문에 잠시 동안 주변에있는 것 같습니다.
Thomas Hunziker

10
답변 해주셔서 감사합니다. 참고 : 데이터베이스 캐스케이드 트리거는 최대 절전 모드를 통해 DDL 생성을 활성화 한 경우에만 생성됩니다. 그렇지 않으면 다른 방법 (예 : liquibase)을 추가하여 'DELETE FROM Parent WHERE id = 1'과 같이 DB에 대해 직접 임시 쿼리를 실행하여 캐스케이드 제거를 수행해야합니다.
mjj1409

1
연결이 @OneToOne문제를 해결하는 방법에 대한 아이디어 가있을 때 작동하지 않습니다 @OneToOne.
stakowerflol

1
@ThomasHunziker 이것은 orphanRemoval에서 작동하지 않습니다.
oxyt

13

다음과 같이 양방향 관계를 만듭니다.

@Entity
public class Parent implements Serializable {

    @Id
    @GeneratedValue
    private long id;

    @OneToMany(mappedBy = "parent", cascade = CascadeType.REMOVE)
    private Set<Child> children;
}

8
큰 아이 세트에서 작동하는 시간의 엄청난 금액을 필요하기 때문에 나쁜 대답, 양방향 관계는 JPA에 끔찍한
Enerccio

1
양방향 관계가 느리다는 증거가 있습니까?
shalama

@enerccio 양방향 관계가 일대일이면 어떨까요? 또한 양방향 관계가 느리다는 기사를 보여주세요. 뭘 천천히? 검색 중? 삭제 하시겠습니까? 업데이트?
saran3h

@ saran3h 모든 작업 (추가, 제거)은 모든 하위 항목을로드하므로 쓸모가 없을 수있는 막대한 데이터로드입니다 (예 : 값을 추가해도이 매핑이 정확히 수행하는 데이터베이스에서 모든 하위 항목을로드 할 필요가 없습니다).
Enerccio

@Enerccio 나는 모든 사람들이 조인에 지연 로딩을 사용한다고 생각합니다. 그렇다면 여전히 성능 문제는 무엇입니까?
saran3h

1

단방향 @ManytoOne에서 보았지만 삭제가 예상대로 작동하지 않습니다. 부모가 삭제되면 이상적으로 자식도 삭제되어야하지만 부모 만 삭제되고 자식은 삭제되지 않고 고아로 남아 있습니다.

사용 된 기술은 Spring Boot / Spring Data JPA / Hibernate입니다.

스프린트 부팅 : 2.1.2.RELEASE

SpringData JPA / Hibernate는 행을 삭제하는 데 사용됩니다.

parentRepository.delete(parent)

ParentRepository는 아래와 같이 표준 CRUD 저장소를 확장합니다. ParentRepository extends CrudRepository<T, ID>

다음은 내 엔티티 클래스입니다.

@Entity(name = child”)
public class Child  {

    @Id
    @GeneratedValue
    private long id;

    @ManyToOne( fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = parent_id", nullable = false)
    @OnDelete(action = OnDeleteAction.CASCADE)
    private Parent parent;
}

@Entity(name = parent”)
public class Parent {

    @Id
    @GeneratedValue
    private long id;

    @Column(nullable = false, length = 50)
    private String firstName;


}

삭제가 작동하지 않는 이유에 대한 해결책을 찾았습니다. 분명히 최대 절전 모드는 mysql Engine -INNODB를 사용하지 않았으므로 mysql이 외래 키 제약 조건을 생성하려면 엔진 INNODB가 필요합니다. application.properties에서 다음 속성을 사용하여 mysql 엔진 INNODB를 사용하도록 스프링 부팅 / 최대 절전 모드를 만듭니다. 따라서 외래 키 제약이 작동하고 따라서 캐스케이드도 삭제됩니다
ranjesh

이전 주석에서 누락 된 속성 사용. 다음은 사용 된 스프링 속성입니다spring.jpa.hibernate.use-new-id-generator-mappings=true spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
ranjesh

참고로, "코드 가 잘못 되었습니다. 참조name= "parent"
alexander

0

이 방법을 사용하여 한 면만 삭제하십시오.

    @ManyToOne(cascade=CascadeType.PERSIST, fetch = FetchType.LAZY)
//  @JoinColumn(name = "qid")
    @JoinColumn(name = "qid", referencedColumnName = "qid", foreignKey = @ForeignKey(name = "qid"), nullable = false)
    // @JsonIgnore
    @JsonBackReference
    private QueueGroup queueGroup;

-1

@Cascade (org.hibernate.annotations.CascadeType.DELETE_ORPHAN)

주어진 주석이 나를 위해 일했습니다. 시도해 볼 수 있습니다

예를 들어 :-

     public class Parent{
            @Id
            @GeneratedValue(strategy=GenerationType.AUTO)
            @Column(name="cct_id")
            private Integer cct_id;
            @OneToMany(cascade=CascadeType.REMOVE, fetch=FetchType.EAGER,mappedBy="clinicalCareTeam", orphanRemoval=true)
            @Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
            private List<Child> childs;
        }
            public class Child{
            @ManyToOne(fetch=FetchType.EAGER)
            @JoinColumn(name="cct_id")
            private Parent parent;
    }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.