관련 포스팅

2023.07.13 - [Spring/JPA] - 스프링) JPA Entity option 지연 로딩/영속성 전이/고아 entity삭제 (23-07-12)

 

스프링) JPA Entity option 지연 로딩/영속성 전이/고아 entity삭제 (23-07-12)

velog에서 이전한 글 입니다. 2023.07.13 - [spring] - 스프링) JPA Entity 연관관계 - 외래 키 주인 (23-07-11) 지난 포스팅에 이어 JPA Entity Option FetchType EAGER/LAZY (지연 로딩) public @interface ManyToOne{ ... FetchType fetc

cornpip.tistory.com


지우고자 하는 데이터가 다른 테이블에서 FK로 사용 중이라면 지울 수 없다.

 

만약 지우려 하는 엔티티가 Device이고, Sensor가 Device FK를 가지고 있다면

  • 연관된 Sensor를 삭제하던가
  • 연관된 Sensor에서 Device FK를 NULL로 만들던가

어떤 방법으로든 먼저 Device에 종속되어 있는 관계성을 끊어야 Device를 지울 수 있다.

 

CascadeType.REMOVE

CascadeType.REMOVE - Device를 delete할 때, 연관된 Sensor를 자동으로 삭제한다.

    // Device.java
    @OneToOne(mappedBy = "device", cascade = CascadeType.REMOVE)
    private Sensor sensor;
    
    // Sensor.java
    @OneToOne
    @JoinColumn(name = "device_id", referencedColumnName = "id")
    private Device device;

일대다도 마찬가지이고 부모쪽에서 쓰면 된다.

 

+) 외래키 주인에서 사용해도 상관없다. 암튼, cascade 코드가 있는 entity를 삭제할 때 연관 entity를 지운다.

 

orphanRemoval = true

REMOVE와 동일하게 부모쪽에서 쓰면 된다.

특성 CascadeType.REMOVE orphanRemoval = true
작동 시점 부모 엔티티가 삭제될 때 자식 엔티티가 부모와의 관계에서 제거될 때
주 삭제 트리거 부모의 삭제 부모와의 관계가 끊어질 때
관계 해제 시 동작 자식 엔티티는 남아 있음 자식 엔티티는 삭제됨
명시적 삭제 필요 여부 부모의 명시적 삭제가 필요 관계가 끊어지면 자동 삭제

 

remove와 orphanRemoval의 차이를 gpt한테 물어보니 위 표를 줬다.

remove는 부모 엔티티 삭제가 트리거이고, 다른 방법으로 관계를 끊으면 고아 엔티티를 삭제하지 않는다.

orphanRemoval는 관계가 끊어지는 게 트리거이고, 어떤 방법이던 관계가 끊기면 고아 엔티티를 삭제한다.

    // Device.java
    @OneToMany(mappedBy = "device", orphanRemoval = true, cascade = CascadeType.PERSIST)
    private List<Sensor> sensors = new ArrayList<>();
    
    // Sensor.java
    @ManyToOne
    @JoinColumn(name = "device_id", referencedColumnName = "id")
    private Device device;

확인해 보자.

    // TestCode
    @Test
    @Transactional
    @Rollback(value = false)
    void delete() {
        Optional<Device> byId = deviceRepository.findById(4L);
        if (byId.isPresent()) {
            Device device = byId.get();
            List<Sensor> sensors = device.getSensors();
            sensors.set(1, null);
            deviceRepository.save(device);
        }
    }

부모 엔티티 삭제가 아닌 null 값을 넣어 관계를 끊어보면.

  • CascadeType.REMOVE는 관계가 끊어져 사용되지 않는 Sensor 를 삭제하지 않고
  • orphanRemoval = true는 고아 상태 Sensor 를 삭제한다.

어떤 비즈니스든 전산화 솔루션을 제공하면 주로 관계형 DB를 사용하고 많은 연관관계가 생긴다.

그러다 보면 연결할 FK를 빼먹거나, 끊어야 할 FK를 안 끊거나 할 수 있다.

복잡한 ERD일수록 CRUD를 재현하는 것도 시간이 걸리게 되는데, 그래도 테스트 짜다보면 꼭 놓치는 부분들이 보이는 것 같다.