Cascade란 영속성 전이를 말한다. Default Casacde는 아무것도 작성되지 않은 상태이고, PERSIST, MERGE, DETACH, REMOVE, REFRESH, ALL을 제공한다.
PERSIST
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Test
@Transactional
void cascadeTest(){
Member member = new Member();
member.setName("A");
memberRepository.save(member);
Team team = new team();
team.setName("ATeam");
teamRepository.save(team);
member.setTeam(team); // 연관관계
memberRepository.save(member);
team.getMembers().add(member);
teamRepository.save(team);
}
- Cascade를 하지 않는다면 save를 각각 2번씩 수행 해야한다.
- member을 save하지 않으면 영속 상태가 되지 않아 member과 team의 관계를 맺을 때 에러가 발생한다. (entity의 relation reference가 DB에 저장 되지 않고 자바 객체이기 때문)
cascade를 사용하면 조금 더 깔끔한 코드를 작성할 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
@Entity @Data @NoArgsConstructor @AllArgsConstructor public class Member { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private Long age; @ManyToOne(cascade = CascadeType.PERSIST) //추가 @ToString.Exclude private Team team; }
- member이라는 entity가 persist될 때 Team도 persist시키는 옵션이다.
cascade를 해주는 것으로 test코드는 아래와 같이 작성할 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13
@Test @Transactional void cascadeTest(){ Member member = new Member(); member.setName("A"); Team team = new Team(); team.setName("ATeam"); member.setTeam(team); memberRepository.save(team); }
MERGE
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Test
@Transactional
void cascadeTest(){
Member member = new Member();
member.setName("A");
memberRepository.save(member);
Team team = new team();
team.setName("ATeam");
teamRepository.save(team);
member.setTeam(team); // 연관관계
memberRepository.save(member);
team.getMembers().add(member);
teamRepository.save(team);
Member member = memberRepository.findById(1L).get();
member.getTeam().setName("BTeam");
memberRepository.save(member);
}
- 영속성 전이를 PERSIST만 설정했기 때문에 변경사항에 대한 전이는 이루어 지지않는다.
@ManyToOne(cascade = {CascadeType.PERISIT, CascadeType.MERGE})
orphanRemoval
- CASACDE.REMOVE와 유사한 느낌
- CASACDE.REMOVE는 상위 객체가 remove가 수행하면 포함하고 있는 객체의 영속성 이벤트를 전파해서 하위 entity까지 remove시켜주는 옵션이다.
- CascadeType.Remove 는 참조를 변경시켜 무결성 오류를 안나게 할 뿐, 그 데이터는 남겨두게 된다.
- orphan removal옵션은 하위 entity를 setter를 통해 null을 넣어주면 db에서도 삭제해주는 옵션이다.
- null로 설정된 값을 DB에서 삭제하지 않으려면 cascade.remove를 사용해야한다.
- 두 엔티티의 관계를 끊을 때, 테이블의 데이터가 계속 남아있기를 바란다면, CascadeType.Remove 만 쓰는 것이고, 테이블의 해당 데이터까지 삭제를 바란다면 orPhanRemoval = true 를 사용하는 것이다.
1
@OneToMany(orphanRemoval = true)
soft Delete
- 상용 DB에서 delete를 query를 사용하는 경우는 거의 없다.
- 특정 flag를 이용해 지워진 표시만 남긴다.
- private boolean deleted;
Flag가 true라면 출력 Query에서 조회 되어서는 안된다. 따라서 repository에 QueryMethod를 작성해서 사용해야 한다.
1 2 3 4 5
public interface MemberRepository extends JpaRepository<Member, Long> { List<Member> findAllByDeletedFalse(); List<Member> findByIdDeletedFalse(); }
하지만 이렇게 모든 method를 작성할 수 없고, 실수로 삭제된 데이터가 노출 되는 상황이 발생할 수 있다. 직접 QueryMethod작성하는 대신 @Where를 사용해 처리할 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
@Entity @Data @NoArgsConstructor @AllArgsConstructor @Where(clause = "deleted = false") //추가 public class Member { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private Long age; private boolean deleted; }
- Where로 처리하면 해당 조건을 항상 체크한다.