⚠️ 경고 ⚠️
본 카테고리, Dev Memo는 필자가 깊게 다루기는 귀찮지만 궁금한 것들을 체험해보고 간단하게 기록을 남기는 공간입니다. 디버깅 노트, 써드파티 라이브러리 사용기, 버전 업데이트, 어이없는 실수, 오탈자 발견 등. 각종 시덥지 않은 내용이 들어 갈 수 있다는 점 참고바랍니다. (우헤헿)
일단 다음과 같은 환경을 갖춘 상태이다.
- 스프링부트 + JPA + Kotlin
- User와 Club 테이블은 양방향 조인
- ID는 Auto Increment
다음은 요구사항이다.
- 클럽에 가입 신청 혹은 신청 승인된 User를 찾는다
- 특정 기간 내 가입 신청한 User들을 찾는다
- 해당 유저들은 클럽별로 묶는다
- 클럽별 가장 최근에 가입 신청한 User를 모아서 Return해준다
고민해보았다. 과연 Spring Data JPA가 제공하는 쿼리 메소드만으로 이 요구사항을 만족시킬 수 있을까? 수시간을 고민했지만 답을 찾을 수 없었다. 네이티브 쿼리를 사용한다고 해도 무리가 있었다. JPA 서브 쿼리의 한계와 from에 등장한 테이블을 서브쿼리에 재등장시킬 수 없는 점 등이 발목을 잡았다. 만약 QueryDSL을 사용했다면 가능했을 수 있지만 당장 개발을 하기 위해서는 다른 대안이 필요했다. 그래서 얻어낸 해결 방법은 쿼리 메소드와 스트림을 합친 방법이다. 혹시 이 방법보다 좋은 방법을 아는 분이 있다면 댓글 등으로 알려주길 바란다.
먼저 기간과 상태를 기준으로 유저를 조회한다. 이를 위한 메소드는 아래와 같다.
interface UserRepository : JpaRepository<User, Long> {
fun findAllByCreatedAtBetweenAndStatusIn(
startAt: LocalDateTime,
endAt: LocalDateTime,
status: List<UserStatus>
): List<User>
}
그룹핑은 스트림을 통해서 해결했다. 먼저 groupBy로 Club별 map으로 묶는다. 다음 mapNotNull로 Null이 아닌 결과값만 가지도록 하였다. Key가 Club이고 Value가 User인 상태에서 maxByOrNull로 Club에서 ID가 가장 큰 User 데이터를 찾는다. 클럽에 신청했을 때 데이터가 생성되므로 ID가 가장 큰 값을 가진 User가 최신에 가입 신청한 유저이다.
fun getRecentList(startDate: LocalDate, endDate: LocalDate): List<User> {
// 해당 기간의 모든 user 조회
val users = userRepository.findAllByCreatedAtBetweenAndStatusIn(
startDate.atStartOfDay(),
endDate.atStartOfDay(),
listOf(REQUESTED, APPROVED)
)
// 개별 클럽의 최신 유저만 고르기
return users.groupBy {
it.club
}.mapNotNull {
it.value.maxByOrNull { user ->
user.id
}
}
}
이렇게 해서 요구사항에 만족하는 최신 유저 데이터를 얻어보았다. 단순히 쿼리 혹은 스트림에서 그룹을 만든다고 하여 원하는 조건을 만족하는 결과를 얻을 수 있는 것이 아님을 알았다. 경우에 따라서는 퍼포먼스가 조금 하락하더라도 복합적인 사용을 염두해야 한다.
🏖 참고자료
- JPQL 서브쿼리, 조건식, 기본 함수
- GROUP BY 최대/최소값을 가진 ROW에 있는 다른 COLUMN 값 구하기
- java map 최대값 key,value 구하기
'Dev Memo' 카테고리의 다른 글
Fig, 맥 터미널 명령어를 자동완성 해주는 도구 (0) | 2023.09.04 |
---|---|
[Kafka] Consum한 뒤 에러가 나는 경우, 재시도를 막는 방법 (1) | 2022.10.05 |
Google IO 2022 "SVD" 퍼즐, A와 B 풀기 (0) | 2022.04.08 |
코틀린 표준 라이브러리에 동일한 클래스가 2개? (0) | 2022.03.22 |
배포를 했는데 세팅한 Profile을 바라보지 않는 것 같다면? (0) | 2022.01.11 |
Comment