[JPA] JPA에서 OrphanRemoval 활용하기

jpa, spring

orphanRemoval #

@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "paper")
private List<PaperAnswer> paperAnswerList;

Example #

아래와 같이 delete test를 실행해보자.

void bookCascadeTest() {
    ...
    Book book2 = bookRepository.findById(1L).get();
    bookRepository.delete(book2); // delete

    System.out.println("books : " + bookRepository.findAll());
    System.out.println("publishers : " + publisherRepository.findAll());
    }

그러면 결과값이 books 만 삭제되고 publishers 는 여전히 결과값이 나오는데, 연관된 publishers 까지 삭제하기 위해서는

@ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE})
@ToString.Exclude
private Publisher publisher;

이렇게 하면 결과값이 아래와 같이 나옴을 알 수 있다.

books : []  
publishers : []
public class Publisher extends BaseEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @OneToMany(orphanRemoval = true)
    @JoinColumn(name = "publisher_id") // 중간 column을 없애기 위하여
    @ToString.Exclude
    private List<Book> books = new ArrayList<>();

    public void addBook(Book book) {
        this.books.add(book);
    }
}

Soft Delete #

하지만 현업에서는 대부분의 경우에는 delete query를 자주 사용하지않고 flag를 사용해서 지웠다라고 표시만 하는 경우가 많다.

private boolean deleted;
insert into book(`id`, `name`, `publisher_id`, `deleted`) values (2, 'Spring Security 초격차 패키지', 1, false);

예를 들면, Book class에 위와 같이 boolean type의 delete flag를 추가하고, 마찬가지로 DB에도 추가해준다.

그리고 BookRepository class에 아래와 같은 method를 추가해주면 false 처리된 즉, 삭제되지 않은 결과값만 출력된다.

List<Book> findAllByDeletedFalse();
List<Book> findByCategoryIsNullAndDeletedFalse();

@Where #

하지만 이는 개발자의 실수나 여러가지 요인으로 인해 불안정한 요소가 있기 때문에 아래와 같은 @Where annotation을 활용한다.

@Where(clause = "deleted = false")
public class Book extends BaseEntity {