항해99 3기

[TIL] 2021.11.24 최종 프로젝트 진행중 - JPA Native Query return 형식 / Java 저번 주 날짜 구하기

na_o 2021. 11. 24. 11:00
728x90

'지난 주 카페 글 중 좋아요를 가장 많이 받은 사람 1명' 에 대한 API 를 만들고 있다

JPA의 기본 기능이 아닌 JPA의 native query를 사용해야할 것 같다

그래서 가장 먼저 쿼리를 만들었다

BETWEEN에는 지난주 월요일, 지난주 토요일 날짜가 들어가야하지만, 현재는 이에 해당하는 데이터가 없기에데이터가 있는 날짜로 범위를 좁혔다(오로지 테스트를 위해)

SELECT
         r.user_id, rl.recipe_id,
         count(rl.user_id) AS like_count
FROM     recipe_like rl, recipe r
WHERE    rl.recipe_id = r.recipe_id
AND      rl.reg_date BETWEEN '2021-11-16 10:36:02.741931' AND '2021-11-23 12:53:13.741931'
GROUP BY rl.recipe_id
ORDER BY like_count DESC
LIMIT    1;

recipe_like 테이블엔 recipe_id, user_id 컬럼이 있다

해당 게시물(=레시피)에 좋아요를 누르면 어느 user가 어느 recipe를 좋아요 눌렀는지 insert 된다

recipe 테이블엔 어느 유저가 글을 작성했는 지 데이터가 있다

 

SQL 결과

특정 시간대에 사용자가 특정 게시물에 좋아요를 누른 개수를 알아내야 하고

그 게시물을 작성한 사용자를 알아내야 하기 때문에 위와 같이 쿼리를 짰다.

 

 

지금까지 한 테이블에 대한 쿼리를 JPA로 짜면 리턴 형식이 예를 들어

List<Board>, List<BoardComment> 등 제너릭에 테이블 이름이 들어갔다

하지만 여기서 문제점은 위의 결과처럼 user_id, recipe_id, like_count 컬럼을 가진 테이블(JPA로는 엔티티)가 없다

어떻게 리턴해야하지? 라는 생각이 들었다

 

https://kwonnam.pe.kr/wiki/java/jpa/nativequery

 

java:jpa:nativequery [권남]

 

kwonnam.pe.kr

다중 Entity를 결과로 받는 Native Query는 @SqlResultSetMapping로 매핑 정보를 지정해야 하며
그 결과는 List<Object[]>로 리턴한다.

라고 적혀있다

그래서 내가 고민하고 있는 상황에 대한 답은 'List<Object[]>로 반환해야한다' 이다

 

 

Object[]가 어떻게 찍히는 지 디버그를 찍어봤다

왜 Object가 아닌 Object[] 일까 라는 생각을 1초정도 했지만 여기서 해결되었다

user_id = 2, recipe_id = 1, like_count = 3 이런 식으로 맵핑되는게 아니라

0번째 = 2, 1번째 = 1, 2번째 = 3 이런 식으로 맵핑되서 나오는 것이다

@SqlResultSetMapping을 쓰라는 이유가 있었다

@SqlResultSetMapping 어노테이션을 쓰지 않으면 바로 위에서 말한 내용처럼 0번째, 1번째, 2번째로 나오기 때문이다

어노테이션을 사용하면 user_id = 2, recipe_id = 1, like_count = 3 이런 식으로 맵핑되어 나오게 할 수 있다

하지만 난 쓰지 않았다

리턴하는 것이 어떤 데이터를 의미하는지 알 수 있겠지만, 어노테이션을 쓰면 하나의 쿼리를 조회하는데 훨씬 더 복잡해보이고 길어보이기 때문이다..ㅎ로직에서 맵핑에 대한 것을 처리했다

//user_Id
BigInteger userIdBigInteger = (BigInteger) object[0];

//recipe_id
BigInteger recipeIdBigInteger = (BigInteger) object[1];

//like_count
BigInteger likeCountBigInteger = (BigInteger) object[2];

이런 식으로 꺼내오면 되기 때문에 문제 없다고 생각한다

 

 

조회 아주 잘 된다


[Java 저번 주 날짜 구하기]

이 글을 참고했다

http://blog.eomdev.com/java/2016/04/01/%EC%9E%90%EB%B0%948%EC%9D%98-java.time-%ED%8C%A8%ED%82%A4%EC%A7%80.html

 

자바8의 java.time 패키지(LocalDate, LocalTime, LocalDateTime 등)

예전에 JPA와 LocalDate, LocalDateTime 사용하기 에서 자바8에서 추가된 새로운 날짜와 시간에 대한 API에 대해서 간단하게 글을 썼었다. 이번에는 자바8에 추가된 날짜와 시간 API에 대해서 조금 더 자세

blog.eomdev.com

위 글을 보고 이렇게 짰다

        String dateStr = "2021-11-24 10:36:02";    //테스트 데이터
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        LocalDateTime now = LocalDateTime.parse(dateStr, formatter);
        
        //저번 주 월요일 
        LocalDateTime lastMonday = now.with(TemporalAdjusters.previous(DayOfWeek.MONDAY));

        //저번 주 일요일
        LocalDateTime lastSunday = lastMonday.plusDays(6);

하지만 오늘이 11/24 일 경우, lastMonday는 가장 최근의 월요일이였던 11/22 이 나온다

지난주 월요일에서 6일을 더했기 때문에 lastSunday는 11/28 이 나온다

가장 최근에 지나간 월요일 말고...! 전 주 월요일을 구하고 싶다

 

 

        String dateStr = "2021-11-24 10:36:02";    //테스트 데이터
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        LocalDateTime now = LocalDateTime.parse(dateStr, formatter);

        //저번 주 월요일
        LocalDateTime lastMonday = now.with(TemporalAdjusters.previous(DayOfWeek.MONDAY)).minusDays(7);

        //저번 주 일요일
        LocalDateTime lastSunday = lastMonday.plusDays(6);

그래서 -7일을 했다 

그랬더니 저번주 월요일인 11/15 가 나왔다

저번주 일요일도 11/21로 나왔다

하지만 더 테스트 해 보아야할 것은

현재가 월요일(11/22)일 때 어떻게 나오는 지 봐야 한다

 

 

        String dateStr = "2021-11-22 10:36:02";    //테스트 데이터
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        LocalDateTime now = LocalDateTime.parse(dateStr, formatter);

        //저번 주 월요일
        LocalDateTime lastMonday = now.with(TemporalAdjusters.previous(DayOfWeek.MONDAY)).minusDays(7);

        //저번 주 일요일
        LocalDateTime lastSunday = lastMonday.plusDays(6);

lastMonday가 11/08이 나온다..!

왜 2주 전 월요일이 나오지???했지만

이걸로 해결이 되었다

 

 

String dateStr = "2021-11-22 10:36:02";    //테스트 데이터
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime now = LocalDateTime.parse(dateStr, formatter);

//저번 주 월요일 : 지난갔던 가장 최근의 월요일에서 7일 빼기
LocalDateTime lastMonday = now.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)).minusDays(7);

//저번 주 일요일 : 지나갔던 가장 최근의 월요일에서 7일 뺀 상태에서 6일 더하기
LocalDateTime lastSunday = lastMonday.plusDays(6);

previousOrSame을 사용한다면 저번 주 월요일은 11/15 가 나온다!

자연스럽게 저번 주 일요일도 11/21 이 나온다

 

previousOrSame은 오늘을 포함해서 지난 요일을 구하는 함수이다

11/22 을 포함해서 지난 월요일을 구했기 때문에 11/08 이 아닌 11/15가 나온다

 


생각 못 했던 게 있다!

바로 위의 내용을 보면 현재 날짜에서 저번주 월요일 ~ 일요일 날짜를 구하는데

날짜는 바뀌었지만 시/분/초는 바뀌지 않았다!

그거까지는 생각 못 했다

String dateStr = "2021-12-01 10:36:02";    //테스트 데이터
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime now = LocalDateTime.parse(dateStr, formatter);

//저번 주 월요일 : 지난갔던 가장 최근의 월요일에서 7일 빼기
LocalDateTime lastMonday = now.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)).minusDays(7)
                                      .withHour(0).withMinute(0).withSecond(0);

//저번 주 일요일 : 지나갔던 가장 최근의 월요일에서 7일 뺀 상태에서 6일 더하기
LocalDateTime lastSunday = lastMonday.plusDays(6).withHour(23).withMinute(59).withSecond(59);

기존 코드에서 withHour, withMinute, withSecond가 추가된 것 뿐이다

저번 주 순위를 매기려면 저번주 월요일 오전 12시부터 일요일 23시 59분 59초까지 범위를 지정해야 한다

수정 전 코드에서 만약 현재 시간이 2021-11-24 20:52:25 라고 가정한다면

저번주 월요일은 2021-11-15 20:52:25, 저번주 일요일은 2021-11-21 20:52:25 로 된다

이렇게 된다면 빠진 시간이 생기는 것이다