Cascade는 연관관계와 전혀 상관없다!!!! 그냥 영속성을 전이시켜주는 것!영속성 전이(CASCADE)엔티티CascadeType.PERSISTCascadeType.MERGECascadeType.REMOVEOrphanRemoval( @OneToMany(orphanRemoval = true) )현업에서 사용하는 delete(Soft delete. flag활용) - @Where
Cascade는 연관관계와 전혀 상관없다!!!! 그냥 영속성을 전이시켜주는 것!
영속성 전이(CASCADE)
- @OneToMany, @ManyToOne등에서 사용 가능
- 특정 엔티티를 영속 상태로 만들 때, 연관된 엔티티도 함께 영속상태로 만들고 싶을때, 사용한다.
- 영속성을 전이하는 것이지 연관관계를 맺어주는 것이랑은 전혀 상관없음!!
public enum CascadeType { ALL, PERSIST, MERGE, REMOVE, REFRESH, DETACH; private CascadeType() { } }
엔티티
class Book{ // 연관관계의 주인 @ManyToOne @JoinColumn("publisher_id") Publisher publisher; } class Publisher{ @Id @GeneratedValue Long id; @OneToMany(mappedBy="publisher") List<Book> books = new ArrayList<>(); }
CascadeType.PERSIST
@Test void bookCascadeTest(){ Book book = new Book(); book.setName("JPA 초격차 패키지"); Publisher publisher = new Publisher(); publisher.setName("패스트캠퍼스"); publisher.addBook(book); book.setPublisher(publisher); // 연관관계 매핑 // 이렇게 publisher를 book에 넣어주었는데, // book은 save하고 publisher를 save해주지 않으면 transient instance reference // error가 발생함 -> CascadeType.PERSIST 사용 bookRepository.save(book); }
- CASCADE를 쓰지 않으면 persist를 둘다 따로 해주어야 함. cascade가 종속이라는 의미이기에 하나를 하면 다른것도 종속되어서 같이 진행된다고 이해하면 됨
CascadeType.MERGE
@Test void bookCascadeTest(){ Book book = new Book(); book.setName("JPA 초격차 패키지"); Publisher publisher = new Publisher(); publisher.setName("패스트캠퍼스"); publisher.addBook(book); book.setPublisher(publisher); bookRepository.save(book); Book book1 = bookRepository.findById(1L).get(); book1.getPublisher().setName("슬로우캠퍼스"); bookRepository.save(book1); // 이렇게 했을 때, CascadeType.MERGE를 적용해주면 // Publisher 정보까지 바뀌지만 그것이 없다면 publisher정보는 바뀌지 않음 // 또는 @Transactional 을 붙여서 dirty checking을 통해서 update하는 방법도 있음 System.out.println("books : " + bookRepository.findAll()); System.out.println("publishers : " + publisherRepository.findAll()); }
CascadeType.REMOVE
@Test void bookCascadeTest(){ Book book2 = bookRepository.findById(1L).get(); bookRepository.delete(book2); // CascadeType.REMOVE 설정 하지 않으면 books만 비어있고 // publisher는 남아 있음. 설정되면 아래의 publisher까지 삭제해줌 System.out.println("books : " + bookRepository.findAll()); System.out.println("publishers : " + publisherRepository.findAll()); }
@Test void bookRemoveCascadeTest(){ bookRepository.deleteById(1L); // publisher까지 삭제함 System.out.println("books : " + bookRepository.findAll()); System.out.println("publishers : " + publisherRepository.findAll()); bookRepository.findAll().forEach(book -> System.out.println(book.getPublisher())); } // bookRepository.deleteById(1L); query // 1) 연관관계 제거 (null로 설정함으로써) update book set publisher_id=null where publisher_id=? // 2) delete from book where id=? // 3) delete from publisher where id=?
OrphanRemoval( @OneToMany(orphanRemoval = true) )
- @OneToOne과 @OneToMany에만 사용할 수 있음
- 부모 엔티티의 컬렉션에서 자식 엔티티의 참조만 제거하면 자식 엔티티가 자동으로 삭제되는 기능
- 참조가 제거된 엔티티는 다른 곳에서 참조하지 않는 고아 객체로 보고 삭제하는 기능 → 이 기능은 참조하는 곳이 하나일 때만 사용해야 함
- 고아 객체 제거 기능은 영속성 컨텍스트를 플러시할 때 적용되므로 플러시 시점에 DELETE SQL이 실행
@Entity public class Parent{ @Id @GeneratedValue private Long id; @OneToMany(mappedBy="parent", orphanRemoval=true) private List<Child> children = new ArrayList<>(); ... } Parent parent1 = em.find(Parent.class, id); parent1.getChildren().remove(0); // 자식 엔티티를 컬렉션에서 제거 // DELETE FROM CHILD WHERE ID=?
현업에서 사용하는 delete(Soft delete. flag활용) - @Where
- 실제로 delete하는 경우는 거의 없이 deleted라는 boolean field를 만들어서 관리를 주로 함
- query method
findAllByDeletedFalse()
를 통해서 delete 되지 않은 entity만 얻어낼 수 있음
- 그러나 이는 빼 먹을 수 있기 때문에, @Where annotation을 추가함
@Where(cluase = "deleted = false") public class Book{ }