웹/백엔드

[Spring] Spring Data JPA Native Query 작성 시 필요한 것들

이민훈 2021. 12. 29. 02:39

Spring Data JPA에서 Native Query를 작성하는 것은 크게 어렵지 않습니다.

 

public interface PostRepository extends JpaRepository<Post, Long> {
    @Query(value = "SELECT * FROM post", nativeQuery = true)
    List<Post> findAllPost();
}

 

위처럼 @Query 어노테이션을 사용하여 쿼리를 입력하고, nativeQuery 옵션을 true로 주시면 됩니다.

 

하지만 해당 쿼리는 findAll 같은 기본 JPA가 자동으로 생성해주는 쿼리를 사용하면 되기 때문에,

 

테이블에서 특정 컬럼만 가져오거나, 여러 테이블을 조인하거나 할 때, Native Query를 사용하게 될 겁니다.

 

public interface PostRepository extends JpaRepository<Post, Long> {
    @Query(value = "SELECT p.id, p.title, p.content, p.views, u.nickname, count(r.post_id) as likes " +
            "FROM post as p " +
            "LEFT JOIN user as u " +
            "ON p.user_id = u.id " +
            "LEFT JOIN recommendation as r " +
            "ON p.id = r.post_id " +
            "GROUP BY p.id", nativeQuery = true)
    List<PostInterface> findAllPost();
}

 

이런 쿼리가 필요하다고 할 때, SELECT된 데이터는 총 6개입니다.

 

u.nickname 이나 count(r.post_id)는 Post 엔티티에 존재하지 않기 때문에, 매핑이 되지 않는데요.

 

이럴 때 Interface를 만들어 매핑 시켜 줄 수 있습니다.

 

public interface PostInterface {
    Long getId();
    String getTitle();
    String getContent();
    int getViews();
    String getNickname();
    int getLikes();
}

 

그런 다음 Service 단에서 Dto로 변환 후 Controller에 반환해 주면 됩니다.

 

    @Transactional(readOnly = true)
    public List<PostDto> readAllPost() {
        return postRepository.findAllPost()
                .stream()
                .map(PostDto::new)
                .collect(Collectors.toList());

 

하지만 여기에 그치지 않고 pagination 작업이 필요한 경우가 있을 텐데요.

 

    @Transactional(readOnly = true)
    public List<PostDto> readAllPost(int page, int size) {
        PageRequest pageRequest = PageRequest.of(page, size);
        return postRepository.findAllPost(pageRequest)
                .stream()
                .map(PostDto::new)
                .collect(Collectors.toList());
    }

 

List로 반환을 하게 되면 추가적인 작업 없이 PageRequest 객체만 파라미터로 받아오게끔 해주면 됩니다.

 

public interface PostRepository extends JpaRepository<Post, Long> {
    @Query(value = "SELECT p.id, p.title, p.content, p.views, u.nickname, count(r.post_id) as likes " +
            "FROM post as p " +
            "LEFT JOIN user as u " +
            "ON p.user_id = u.id " +
            "LEFT JOIN recommendation as r " +
            "ON p.id = r.post_id " +
            "GROUP BY p.id", nativeQuery = true)
    List<PostInterface> findAllPost(PageRequest pageRequest);
}

 

데이터의 총개수나 전체 페이지 수 등을 알기 위해선 Page로 반환을 해야 합니다.

 

이때 반환형만 Page로 바꿔주게 되면 에러가 발생하게 됩니다.

 

org.h2.jdbc.JdbcSQLSyntaxErrorException: Column "P" not found; SQL statement:

select count(p) FROM post as p LEFT JOIN user as u ON p.user_id = u.id LEFT JOIN recommendation as r ON p.id = r.post_id GROUP BY p.id [42122-200]

 

저는 해당 에러가 떴었는데, 찾아보니 Native Query를 사용할 경우 개수를 세는 countQuery를 따로 적어줘야 합니다.

 

public interface PostRepository extends JpaRepository<Post, Long> {
    @Query(value = "SELECT p.id, p.title, p.content, p.views, u.nickname, count(r.post_id) as likes " +
            "FROM post as p " +
            "LEFT JOIN user as u " +
            "ON p.user_id = u.id " +
            "LEFT JOIN recommendation as r " +
            "ON p.id = r.post_id " +
            "GROUP BY p.id",
            countQuery = "SELECT * FROM post", nativeQuery = true)
    Page<PostInterface> findAllPost(PageRequest pageRequest);
}

 

프로젝트에 Native Query를 적용시키다 겪은 시행착오에 대해 적어봤는데,

 

틀린 점이 있다면 언제든지 알려주시면 감사하겠습니다.