Java & Spring

[JPA] Projection - 필요한 특정 필드만 조회하여 쿼리 최적화 수행

ju_young 2024. 1. 16. 20:52
728x90

먼저 다음과 같이 ArticleFile이라는 엔티티가 있다고 해보자.

@Entity  
public class ArticleFile {  
    @Id  
    @GeneratedValue(strategy = GenerationType.IDENTITY)  
    private Long id;  
    private Long byteSize;  
    private String fileName;
    private String fileExtension;

    @OneToOne(fetch = LAZY, cascade = CascadeType.ALL)  
    @JoinColumn(name = "article_id")  
    private Article article;

    ...
}

 

ArticleFile에는 id를 제외하고 byteSize, fileName, fileExtension이 있다.

 

현재 목적은 게시글을 삭제하고 게시글에 포함된 파일을 Amazon S3에서 삭제하는 것이다.

모든 필드를 조회할 경우

먼저 JpaRepository에 게시글 id를 받아서 articleFile을 조회하는 쿼리 메소드를 추가해보자.

public interface ArticleFileRepository extends JpaRepository<ArticleFile, Long> {  
    Optional<ArticleFile> findByArticleId(Long articleId);  
}

 

추가한 쿼리 메소드를 사용하면 다음과 같이 Amazon S3에 저장된 파일을 삭제할 수 있을 것이다.

@Transactional
public void deleteArticleFile(Long articleId) {
    ArticleFile articleFile = articleFileRepository.findByArticleId(articleId)
        .orElseThrow(EntityNotFoundException::new);
    String fileName = articleFile.getFileName();
    S3Service.deleteFile(fileName);
}

 

하지만 이때 findByArticleId를 통해 생성된 쿼리는 ArticleFile의 모든 필드(byteSize, fileName, fileExtension)를 포함하게된다. 즉, fileName을 조회하기위해 모든 필드를 불러오는 비효율적인 쿼리가 생성되는 것이다.

필요한 특정 필드만 조회

특정 필드만 조회할 경우 간단하게 다음과 같이 인터페이스를 추가해주기만 하면된다.

public interface FileNameInterface {
    String getFileName();
}

 

Repository에는 메소드 구분을 위해 다음과 같이 findFileNameByArticleId라고 정의했다.

public interface ArticleFileRepository extends JpaRepository<ArticleFile, Long> {  
    Optional<FileNameInterface> findFileNameByArticleId(Long articleId);
}

 

이제 이렇게 새로 생성한 쿼리 메소드를 사용한다면 다음과 같이 코드를 작성할 수 있다.

@Transactional
public void deleteArticleFile(Long articleId) {
    s3Service.deleteFile(searchFileName(articleId));
}

private String searchFileName(Long articleId) {
    return articleFileRepository.findFileNameByArticleId(articleId)
            .orElseThrow(EntityNotFoundException::new)
            .getFileName();
}

 

이전처럼 모든 필드를 조회하고 파일명을 가져오는 로직보다 좀 더 명확하고 쿼리를 최적화할 수 있었다.

select
    articlefil0_.file_name as col_0_0_,
from
    article_file articlefil0_ 
left outer join
    article article1_ 
        on articlefil0_.article_id=article1_.id 
where
    article1_.id=?
728x90