[Spring JPA] JPQL 에서 Interface로 선언된 DTO 사용하기

춤추는 머쉬룸·2024년 2월 29일
0

2024 CNU NHN ACADEMY

목록 보기
9/15
post-thumbnail

Spring Data JPA 의 메소드 네임 쿼리를 사용하기 어려운 복잡한 쿼리의 경우, JPQL 이나 QueryDsl 를 사용한다.
그 중에서 문자열로 쿼리를 날리는 JPQL 을 연습하고 있었다... JPQL 은 날쿼와는 다르게 데이터베이스에 상관 없이 똑같은 SQL 문을 사용한다.


그런데 문제! DTO 로 결과값을 반환하려고 하는데, 결과값이 기대하는 배열의 길이와는 맞지만, 모든 값이 null 이 뜨는 오류가 생겼다.


1. 첫번째 삽질

// n번 이상 별점 n점 이상을 받은 제품
    @Query(value =
            "SELECT p" +
                    " FROM Product p" +
                    " WHERE p.productId IN (SELECT r.product.productId" +
                    "                       FROM Review r" +
                    "                       WHERE r.rating >= ?1 " +
                    "                       GROUP BY r.product " +
                    "                       HAVING COUNT(r) >= ?2)"
    )
    List<ProductDto> findProductByRatingGreaterThanEqual(Integer rating, long howMuch);

위에 보이는 것 처럼 배열은 잘 가져왔지만 값이 null이었다. 200OK 라서 오류 메세지도 제대로 안나오기 때문에 대체 어디가 잘못 되었는지를 알 수가 없었다...


2. 두번째 삽질

// n번 이상 별점 n점 이상을 받은 제품
    @Query(value =
            "SELECT p.productId, p.category, p.modelNumber, p.modelName, p.productImage, p.unitCost, p.description" +
                    " FROM Product p" +
                    " WHERE p.productId IN (SELECT r.product.productId" +
                    "                       FROM Review r" +
                    "                       WHERE r.rating >= ?1 " +
                    "                       GROUP BY r.product " +
                    "                       HAVING COUNT(r) >= ?2)"
    )
    List<ProductDto> findProductByRatingGreaterThanEqual(Integer rating, long howMuch);

DTO 와의 매핑의 문제? 혹은 순서가 랜덤이라서 그런가 싶어 각 항목의 이름을 제대로 명시해주었다.
하지만... 위의 오류와 똑같이 모든 값이 null 이 나왔다.


3. 해결

// n번 이상 별점 n점 이상을 받은 제품
    @Query(value =
            "SELECT p.productId as productId, p.category as category, " +
                    "p.modelNumber as modelNumber, p.modelName as modelName, " +
                    "p.productImage as productImage, p.unitCost as unitCost, " +
                    "p.description as description" +
                    " FROM Product p" +
                    " WHERE p.productId IN (SELECT r.product.productId" +
                    "                       FROM Review r" +
                    "                       WHERE r.rating >= ?1 " +
                    "                       GROUP BY r.product " +
                    "                       HAVING COUNT(r) >= ?2)"
    )
    List<ProductDto> findProductByRatingGreaterThanEqual(Integer rating, long howMuch);

문제는 as 였다.

as 를 써주지 않아서 엔티티와 제대로 매핑이 안된 것이었다! 가져오기는 했는데 어떤 곳에 매핑해야 할지 몰라서 null 을 써준 것 같다...

GET http://localhost:8080/product?rating=3&howMuch=2

HTTP/1.1 200 
Content-Type: application/json
Transfer-Encoding: chunked
Date: Thu, 29 Feb 2024 11:31:43 GMT
Keep-Alive: timeout=20
Connection: keep-alive

[
  {
    "description": "this is product 1",
    "unitCost": 5000,
    "modelNumber": "123A",
    "modelName": "product1",
    "category": [
      {
        "categoryId": 1,
        "categoryName": "Book"
      }
    ],
    "productId": 1,
    "productImage": "product1.png"
  },
  {
    "description": "this is product 3",
    "unitCost": 5000,
    "modelNumber": "123A",
    "modelName": "product3",
    "category": [
      {
        "categoryId": 1,
        "categoryName": "Book"
      }
    ],
    "productId": 3,
    "productImage": "product3.png"
  }
]
Response file saved.
> 2024-02-29T203143.200.json

Response code: 200; Time: 979ms (979 ms); Content length: 385 bytes (385 B)

드디어 원하는 결과가 나왔다!!!!!!


4. 새로 알게된 사실

  1. JPQL 을 사용 할 때는 꼭 as(컬럼명) 을 써야한다.
  2. HAVING COUNT() 함수는 long 타입 의 숫자와 비교한다.

0개의 댓글