일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 스프링 핵심 원리
- http
- 인프런
- JPQL
- java
- spring
- SpringBoot
- kotlin
- 그리디
- springdatajpa
- 김영한
- QueryDSL
- 백준
- AOP
- JDBC
- Spring Boot
- transaction
- db
- jpa
- Thymeleaf
- pointcut
- Android
- Servlet
- 알고리즘
- 자바
- Exception
- 스프링 핵심 기능
- Greedy
- Proxy
- 스프링
- Today
- Total
개발자되기 프로젝트
Cascade (영속성 전이) 본문
1. Cascade란?
영속성 전이로, Entity의 상태(생애주기)의 변화가 있을 때 연관 관계의 entity에 상태변화(생애주기)를 전파한다.
즉 Cascade는 연관관계의 entity에 영속성을 전이시켜준다.
따라서 @OneToOne, @OneToMany 등 과 같이 연관관계가 있을 때 설정할 수 있다.
2. Cascade 설정 6가지
기본 상태는 영속성 전이 없음.
ALL | 아래 속성 모두 포함. 항상 영속성 전이. |
PERSIST | 부모 persist시 연관 entity도 persist |
MERGE | Transaction종료 후 detach 상태에서 연관 entity 수정/추가 후 부모 entity가 merge()를 하면 연관 entity의 변경, 추가내용 저장됨. |
REMOVE | 삭제 시 연관 entity도 삭제 |
REFRESH | entity를 다시 로드했을 때, 연관관계 entity도 같이 로드 |
DETATCH | 영속성 관리하지 않겠다. detatch 시점에 함께 detach |
public enum CascadeType { /** Cascade all operations */ ALL, /** Cascade persist operation */ PERSIST, /** Cascade merge operation */ MERGE, /** Cascade remove operation */ REMOVE, /** Cascade refresh operation */ REFRESH, /** * Cascade detach operation * * @since 2.0 * */ DETACH }
연관관계를 활용해야 한다. book, publisher를 이용한다!
Book과 Publisher의 연관관계는 N : 1 이다.(Book : Publisher = Many : one)
Setter를 통해 연관관계를 맺어주고, publicshet에서 book을 주입할 때는 list로 입력해야 한다.
따라서 getBooks를 통해 book list를 가져오고 add를 해주는 방법으로 진행한다.
하지만 영속성 전이 test를 위해 publisher에 book 추가 및 save는 주석처리한다.
<BookRepositoryTest>
@Transactional @Test public void bookCascadeTest(){ Book book = new Book(); book.setName("JPA 공부하자"); Publisher publisher = new Publisher(); publisher.setName("패스트캠"); book.setPublisher(publisher); bookRepository.save(book); //publisher.getBooks().add(book); //set이 더 직관적임 ㅋㅋㅋㅋ //publisher.addBook(book); //publisherRepository.save(publisher); System.out.println("books : " + bookRepository.findAll()); System.out.println("publishers : " + publisherRepository.findAll()); } }
<Publisher>
public void addBook(Book book){ this.books.add(book); }
이 상태로 test를 돌리면 에러가 난다.
object references an unsaved transient instance
Book, Publisher가 영속성 관리가 되지 않은 상태에서, Book에 Publisher를 넣으면 연관관계를 맺어주기 힘들기 때문.
이 상태에서 cascade옵션을 넣어주자!
3. PERSIST
<Book> : PERSIST(=Insert)에 대해 영속성 전이를 일으키겠다.
Book이 PERSIST될 때 Publisher도 PERSIST시켜라!
@ManyToOne(cascade = CascadeType.PERSIST) @ToString.Exclude private Publisher publisher;
실행 결과 publicsher를 save하지 않았는데 insert query가 실행됨
book이 PERSIST 될 때 publisher도 같이 PERSIST 되었다.
Hibernate: insert into publisher (created_at, updated_at, name) values (?, ?, ?)
4. MERGE
publisher가 영속화 된 후 다시 불러와서 수정 & save() 해보자.
@Transactional @Test public void bookCascadeTest(){ Book book = new Book(); book.setName("JPA 공부하자"); Publisher publisher = new Publisher(); publisher.setName("패스트캠"); book.setPublisher(publisher); bookRepository.save(book); System.out.println("books : " + 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이 Persist될 때만 Publisher도 PERSIST 될 것 같은데..?
Publisher가 udpate되었다...?
Hibernate: update publisher set created_at=?, updated_at=?, name=? where id=? Hibernate: select publisher0_.id as id1_4_, publisher0_.created_at as created_2_4_, publisher0_.updated_at as updated_3_4_, publisher0_.name as name4_4_ from publisher publisher0_ publishers : [Publisher(super=BaseEntity(createdAt=2021-07-06T21:50:38.417027900, updatedAt=2021-07-06T21:50:38.650754200), id=1, name=탐사수, books=[])]
사실 Cascade 옵션을 배열로 여러 개를 넣을 수 있는 걸 보려고 했는데...
@ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
원인은 lazyInitializationException을 처리하려고 추가한 @Transactinal인 것 같다.
* 만약 method에 @Transactional이 적용되면
jpql(findAll())실행 시 commit 후 select query가 실행된딘. 따라서 위의 결과는 autoFLush에 의해 해당 내용이 DB에 반영되고 select query가 실행된 결과이다.
* 해결
1) bookCascadeTest()의 @Transaction을 지우고
2) <Publisher> : Publisher에서 books에 대해 순환참고 끊기 위해 @ToString.Exclude 적용
@OneToMany @JoinColumn(name = "publisher_id") @ToString.Exclude private List<Book> books = new ArrayList<>();
3) <Book> : MERGE 삭제
@ManyToOne(cascade = {CascadeType.PERSIST}) @ToString.Exclude private Publisher publisher;
4)결과 : 의도한 대로 결과가 나왔다. PERSIST는 MERGE OPERATION에 대해 대응이 안된다.
publishers : [Publisher(super=BaseEntity(createdAt=2021-07-06T22:38:26.183082, updatedAt=2021-07-06T22:38:26.183082), id=1, name=패스트캠)]
5) <Book>에서 cascade 옵션에 MERGE를 추가하면.
@ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
book의 Merge operation에 대해 publisher도 merge가 된다.
5. GitHub : 210706 Cascade