📄 Describe
작업 대상
지금까지 별 생각없이 JPA의 일 대 다 관계, 엔티티들에 관해 JPA
의 deleteAll()
를 메소드를 사용했었다.
하지만, API 테스트 중 로그의 쿼리를 확인하니 관련있는 엔티티 개수만큼 delete
쿼리가 나가는 것을 확인했다.
FitTrip의 커뮤니티 서비스 기준 포럼 : 파일 -> 일 : 대 관계애서 포럼 한개를 삭제했을 때 파일의 개수만큼 delete 쿼리가 나간 것이다.
이는 만약 한 포럼에 수십, 수백개의 파일이 존재한다면 쿼리 성능에 문제가 있을 수 있겠다는 생각이 들었다.
따라서, 단순하게 JPA의 deleteAll()
를 사용하는 것이 아닌 IN을 사용하여 배치 삭제를 해야겠다는 생각이 들었다.
deleteAll() 예시 사진
✅ Tasks
작업 세부 내용
위에서 설명한 것처럼 기존 JPA deleteAll()
방식 대신에 IN
을 사용한 방법으로 새롭게 구현한 과정이다.
기존 코드
private void fileDelete(List<File> files) {
files.stream()
.map(File::getFileUrl)
.forEach(fileUploadService::delete);
List<Long> fileIds = files.stream()
.map(File::getId)
.toList();
fileRepository.deleteAll();
}
변경한 코드
private void fileDelete(List<File> files) {
files.stream()
.map(File::getFileUrl)
.forEach(fileUploadService::delete);
List<Long> fileIds = files.stream()
.map(File::getId)
.toList();
fileRepository.deleteAllByIdIn(fileIds);
}
public interface FileRepository extends JpaRepository<File, Long> {
@Modifying
@Query("delete from File f WHERE f.id IN :ids")
void deleteAllByIdIn(@Param("ids") List<Long> ids);
}
하지만, 이렇게 했을 때 아래와 같은 오류가 발생했다.
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)
다른 트랜잭션에서 해당 로우가 이미 수정되거나 삭제되었다는 오류가 발생했다.
이런 오류가 뜬 원인을 생각하다가 CASCADE
가 혹시 문제가 되지 않을까라는 생각이 들었다.
나는 포럼이 삭제되었을 때 File도 같이 삭제되기를 원했기 때문에 orphanRemoval=true
설정을 사용했었다.
하지만, 해당 설정으로 인해 IN
으로도 삭제가 되고 orphanRemoval
에 의해 Forum에 의한 삭제도 시도한다.
따라서, 위 오류의 원인이 해당 설정이라는 생각이 들었고 다음과 같이 수정했다.
기존 코드
@OneToMany(mappedBy = "forum", orphanRemoval = true)
@OneToMany(mappedBy = "forum")
private List<File> files = new ArrayList<>();
변경한 코드
// @OneToMany(mappedBy = "forum", orphanRemoval = true)
@OneToMany(mappedBy = "forum")
private List<File> files = new ArrayList<>();
이렇게 설정하니 문제없이 IN
으로 인한 Batch
삭제가 가능해졌다.
🙋🏻 More
나는 직접 @Query
를 사용하여 배치 삭제를 위한 코드를 작성했지만, JPA에 이미 해당 메소드가 존재한다는 것을 깨달았다.
- deleteAllInBatch()
- deleteAllByIdInBatch()
참고 📢
만약, 팀원 분들이 deleteAll()
를 사용하고 계셨다면 위의 두 메소드를 이용하는 것이 좋을 것 같습니다.
하지만, 실제 서비스에서는 문제가 생길 우려가 다분한 CASCADE
사용을 지양한다고 합니다.
첨부파일 사진 출처(FitTrip과는 무관)
https://github.com/haero77/Today-I-Learned/assets/65555299/ae33f36c-4129-49a1-8428-8d03d980079f
'프로젝트 > FitTrip' 카테고리의 다른 글
[트러블슈팅] SSE 적용시 게이트웨이와 유저 서비스 연동간 발생 오류 해결 (0) | 2024.06.25 |
---|---|
[트러블슈팅] 무중단 배포간 Docker Compose 오류 해결 (2) | 2024.06.25 |
[트러블슈팅] 서버 배포간 다양한 CORS 오류 (0) | 2024.06.25 |
[트러블슈팅] API Gateway OpenFeign 사용 오류 (0) | 2024.06.25 |
[트러블슈팅] OpenFeign과 Spring Cloud Gateway간의 순환 참조 오류 (0) | 2024.06.25 |