JPA에서 OrphanRemoval 활용하기
- 이전에 포스트 했던 JPA Cascade에서 Remove는 없었다. Remove에 대한 설명이 많기 때문이다. 우선 Remove Cascade를 사용해보자.
CascadeType.REMOVE
Book book2 = bookRepository.findById(1L).get();
bookRepository.delete(book2);
// bookRepository.deleteById(1L);
// publisherRepository.delete((book2.getPublisher()));
System.out.println("books : " + bookRepository.findAll());
System.out.println("publishers : " + publisherRepository.findAll());
이전에 만들 Test 코드에 Book을 삭제하는 코드를 작성하고 Test하면 Book은 지워지지만 그에 대한 publisher는 지워지지않는다. 그래서 Book 엔티티에 CascadeType.REMOVE 추가해주자.
@ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE} ) @ToString.Exclude private Publisher publisher;
그럼 Book과 publisher가 삭제 될 것이다.
publisher가 여러개의 연관관계를 맺고 있으면 어떻게 될까?
먼저 Data.sql에 insert문을 추가하자.
insert into publisher(`id`,`name`) value (1, '빠른대학'); insert into book(`id`,`name`,`publisher_id`) values (1,'JPA 작은격차 클래스', 1); insert into book(`id`,`name`,`publisher_id`) values (2,'Spring Security 작은격차 클래스', 1);
Test
@Test void bookRemoveCascadeTest() { bookRepository.deleteById(1L); System.out.println("books : " + bookRepository.findAll()); System.out.println("publishers " + publisherRepository.findAll()); bookRepository.findAll().forEach(book -> System.out.println(book.getPublisher())); }
book이 지워지면서 publisher도 지워지긴 하는데 update문이 실행된다. setter를 통해 연관관계가 제거하려고 null로 주입이된다. 여기서 OrphanRemoval를 사용한다.
OrphanRemoval(고아제거속성)
@Test
void bookCascadeTest() {
Book book = new Book();
book.setName("JPA 초격차 패키지");
Publisher publisher = new Publisher();
publisher.setName("빠른대학");
book.setPublisher(publisher);
bookRepository.save(book);
System.out.println("book : " + bookRepository.findAll());
System.out.println("publishers : " + publisherRepository.findAll());
Book book1 = bookRepository.findById(1L).get(); // 학습용 코드 실무에서는 이렇게 안함.
book1.getPublisher().setName("느린대학");
bookRepository.save(book1);
System.out.println("publishers : " + publisherRepository.findAll());
Book book2 = bookRepository.findById(1L).get();
bookRepository.delete(book2);
Book book3 = bookRepository.findById(1L).get();
book3.setPublisher(null);
bookRepository.save(book3);
System.out.println("books : " + bookRepository.findAll());
System.out.println("publishers : " + publisherRepository.findAll());
System.out.println("book3 : " + bookRepository.findById(1L).get().getPublisher());
}
- book3.setPublisher(null); 하는거 처럼 하면 연관관계를 끊는게 된다.
- 하지만 연관관계를 지웠지만 Publisher는 존재한다. 이때 OrphanRemoval(고아제거속성)을 사용한다.
- cascade는 말 그대로 상위객체가 remove하게되면 포함하고 있는 객체의 해당 영속성 이벤트를 전파헤서 하위 Entity까지 remove해준다. 하지만 연관관계가 끊어지면 Remove 이벤트가 실행되지않는다. 단순히 set null 할때 orphanRemoval=false하면된다. 만약 OrphanRemoval=true로 하면 null값으로 변경되면서 그 하위 Entity까지 삭제가 된다. 한번 걸어보자.
@OneToMany(orphanRemoval = true) @JoinColumn(name = "publisher_id") @ToString.Exclude private List<Book> books = new ArrayList<>();
softDelete
Cascade와 상관없지만 Delete에 대한 꿀팁이다.
상용서비스에서는 일반적으로 Data를 Delete 쿼리를 사용하는 경우는 많이 없다. 물론 법적요건때문에 회원을 delete하긴하는데 일반적으로 일종의 flag를 이용하여 "지웠다" 라고 인식하게만 한다.
예를 들면 private boolean deleted; 으로 하여 deleted가 true가 되면 삭제된 데이터가 되는 것이다.
예를 들어 insert into book(`id`,`name`,`publisher_id`, `deleted`) values (1,'JPA 작은격차 클래스', 1,false); insert into book(`id`,`name`,`publisher_id`, `deleted`) values (2,'Spring Security 작은격차 클래스', 1,false); insert into book(`id`,`name`,`publisher_id`, `deleted`) values (3,'SpringBoot 하나인 클래스', 1,true); //삭제된 data
그럼 조회를 했을때 나오면 안된다.
public interface BookRepository extends JpaRepository<Book, Long> { List<Book> findByCategoryIsNull(); }
@Test void softDelete(){ bookRepository.findAll().forEach(System.out::println); System.out.println(bookRepository.findById(3L)); bookRepository.findByCategoryIsNull().forEach(System.out::println); }
위 Test에서 deleted가 true이면 결과값이 나오면 안된다. 이럴때는 어떻게 할까?
public interface BookRepository extends JpaRepository<Book, Long> { ... ... ... List<Book> findAllByDeletedFalse(); List<Book> findByCategoryIsNullAndDeletedFalse(); }
BookRepository에 findAllByDeletedFalse() Deleted가 False인값만 부르는 메서드를 만들어 써야한다.
그럼 항상 이렇게 DeletedFalse 메서드를 계속 만들어 줘야할까? 아래방법으로 하면 된다.
@Where(clause = "deleted =false")
public class Book extends BaseEntity {
}
- 이렇게 Where 조건을 달면 Entity에 관한 모든쿼리에 Where deleted =false가 붙어서 나온다.
Cascade와 OrphanRemoval 그리고 flag를 이용한 delete를 배웠다. 이중 flag를 이용한 delete는 꿀팁이다!!!.
'Dev > JPA' 카테고리의 다른 글
JPA Native Query (0) | 2022.03.02 |
---|---|
JPA @Query (0) | 2022.03.02 |
JPA에서 Transaction 활용하기 (0) | 2022.03.02 |
JPA Cascade (0) | 2022.03.02 |
JPA에서 Transaction의 전파 (0) | 2022.03.02 |