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를 적용시키다 겪은 시행착오에 대해 적어봤는데,
틀린 점이 있다면 언제든지 알려주시면 감사하겠습니다.
'웹 > 백엔드' 카테고리의 다른 글
[TypeORM] 커스텀 값 entity에 매핑하여 들고오는 법 (1) | 2024.02.07 |
---|---|
[Spring] Spring Data JPA PageRquest 관련 에러 (0) | 2021.12.30 |
댓글