⚡ 개요
내부에서 JPA를 사용하면서 entity에서 Cascade 옵션을 사용한 개발자분이 있었고, 이때 일부 데이터가 아무 값 없이 생성이 되거나 삭제되는 이슈가 발생을 했다. 다행히 문제점이 Cascade에 있다고 판단을 해서 수정을 했지만 실제 고객사에 반영이 되었다면 큰 문제가 발생했을것이다. 그래서 사용시 주의 사항 및 내용을 정리 하려고 한다.
📒 Cascade (영속성 전이)
특정 엔티티를 영속상태로 만들 때 연관관계에 있는 엔티티도 함께 영속상태로 만들기 위해서 사용한다.
부모 엔티티를 다룰때 연관 되어있는 자식 엔티티까지 다룰수 있게 해준다.
JPA에서는 Cascade 을 통해서 영속성을 다룰수 있도록 도와준다. 이와 관련된 옵션들이 존재한다.
- CascadeType.ALL: 모든 Cascade를 적용
- CascadeType.PERSIST: 엔티티를 영속화할 때, 연관된 엔티티도 함께 유지
- CascadeType.MERGE: 엔티티 상태를 병합(Merge)할 때, 연관된 엔티티도 모두 병합
- CascadeType.REMOVE: 엔티티를 제거할 때, 연관된 엔티티도 모두 제거
- CascadeType.DETACH: 부모 엔티티를 detach() 수행하면 연관 엔티티도 detach()상태가 되어 변경 사항 반영 X
- CascadeType.REFRESH: 상위 엔티티를 새로고침(Refresh)할 때, 연관된 엔티티도 모두 새로고침
⚡ 내용
기본적인 사용 방법에 대한 샘플 코드이다.
@Getter
@Setter
@Entity
@Table(name = "sample_table")
public class sample_table
{
@Id
@Column(name = "sample_key")
private Long sampleKey;
@Column(name = "sample_note")
private Long sampleNote;
@Column(name="REG_DT", updatable = false)
private Timestamp regDt;
@Column(name="REG_KEY", updatable = false)
private Long regKey;
@Column(name = "UPD_DT")
private Timestamp updDt;
@Column(name = "UPD_KEY")
private Long updKey;
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinColumn(name = "REG_KEY", referencedColumnName = "USER_KEY")
@NotFound(action = NotFoundAction.IGNORE)
private user_info user_info;
}
실수 했던 내부 entity의 일부를 샘플 코드로 구현한 내용 이다.
entity를 설계하는 과정에서 등록자의 정보를 가져오는 과정에서 @ManyToOne 어노테이션을 사용했고, CascadeType.ALL 옵션을 사용 했다.
이렇게 사용을 하게 되면 어떻게 되겠는가?
당연히 문제가 발생한다. 샘플 데이터를 추가 하거나, 삭제 하는 과정에서 유저 테이블에 영속성 전이를 시켜서 같이 변경 된다.
샘플 데이터을 넣을때 등록자를 빼놓고 넣는다면, 사용자 테이블에 row가 추가될것이고, 샘플 데이터를 삭제 한다면 사용자 테이블에서 연관된 데이터가 삭제가 되는 이슈가 발생한다.
일단 이렇게는 절대 사용하지 않는다고 치고, 현재 상황에서 어떻게 코드를 수정 해야 하는가 ?
@Getter
@Setter
@Entity
@Table(name = "sample_table")
public class sample_table
{
@Id
@Column(name = "sample_key")
private Long sampleKey;
@Column(name = "sample_note")
private Long sampleNote;
@Column(name="REG_DT", updatable = false)
private Timestamp regDt;
@Column(name="REG_KEY", updatable = false)
private Long regKey;
@Column(name = "UPD_DT")
private Timestamp updDt;
@Column(name = "UPD_KEY")
private Long updKey;
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinColumn(name = "REG_KEY", referencedColumnName = "USER_KEY", insertable = false, updatable = false)
@NotFound(action = NotFoundAction.IGNORE)
private user_info user_info;
}
내부적으로는 @JoinColumn 어노테이션의 옵션을 사용해서 처리 했다.
위의 방법으로 처리를 하는게 정답도 아닐뿐더러 속도적인 부분도 체크를 하지 못했던 방법이다.
Cascade옵션을 사용할 경우 고려해야 할 점을 참고 해보면 '통상적으로 권장하는 cascade 범위는, 완전히 개인 소유하는 엔티티일 때, 예를 들어서 게시판과 첨부파일이 있을 때 첨부파일은 게시판 엔티티만 참조하므로, 개인 소유하는 경우에는 사용 가능'이라고 한다.
(잘 알고 사용하자..😅)
⚡ 참고
https://resilient-923.tistory.com/417
'Java' 카테고리의 다른 글
[JPA] QueryDsl에서 동적 쿼리 및 배치 쿼리 사용 방법 (0) | 2024.03.30 |
---|---|
[JAVA] 날짜 변환 하기 (Date, Time, LocalDate, Timestamp등) (0) | 2024.01.21 |
[JAVA] Connection leck 관련 Apparent connection leak detected 경고 관련 내용 (0) | 2023.12.29 |
[Pythod] 파이썬 개발 도전 (2) (0) | 2023.12.10 |
[JAVA] java 21 LTS 릴리즈 내용 정리 (0) | 2023.09.20 |