잘못된 점이나 부족한 부분이 있다면 언제든 지적 부탁드립니다
0️⃣ 개요
이번 프로젝트에서 게시글을 날짜별/월별/요일별로 통계 등등 통계 관련 api 작업을 맡게 되었다.
DB에 시간은 모두 timestamp로 저장되어 있었기 때문에, api 작업을 하기 위해서 시간들을 date format 하는 작업이 꼭 필요했다.
- 개발용 DB는 h2 를 사용했고, 배포할 때에는 Mysql을 사용하고 있다.
1️⃣ 데이터 베이스 내장 함수 사용하기
처음엔 Mysql 에 있는 date를 다루는 다양한 함수가 있으니까 이걸 잘 활용하면 되겠군 ! 이라고 생각했다.
첫 시도는, 블로그를 참고 하여서 querydsl에서 date_format()을 사용해 month만 추출한 뒤 group by 해보았다.
// # 월별 조회
@Override
public List<FindMonthRecordResponse> findMonthRecordByMemberId(Long memberId) {
StringTemplate toMonth = stringTemplate("DATE_FORMAT({0}, '%m')", board.createdAt);
return queryFactory
.select(fields(FindMonthRecordResponse.class,
toMonth.when(1).then("jan")
.when(2).then("feb")
.when(3).then("mar")
.when(4).then("apr")
.when(5).then("may")
.when(6).then("jun")
.when(7).then("jul")
.when(8).then("aug")
.when(9).then("sept")
.when(10).then("oct")
.when(11).then("nov")
.when(12).then("dec")
.otherwise("기타").max().as("month"),
toMonth.count().as("countMonth")
))
.from(board)
.where(board.member.id.eq(memberId))
.groupBy(toMonth)
.distinct()
.fetch();
}
그런데 안된다 ..
여러 Dialect를 다 뒤져 봤는데 date_format()을 지원하는 버전이 많지 않았다. 따라서 나는 이유를 해당 함수를 지원하지 않아서 .. 라고 생각하고 있다.
Dialect도 모든 버전을 다 해봤는데 안된다. Mysql mode 설정하는 것도 해봤다. 안된다.
나와 100% 같은 경우는 아니었지만, 영한 선생님께서는 기능의 한계로 동작하지 않을 수도 있다고 하셨다 ..!그런데 다른 블로그 글들 보면 다들 사용했던데 ,, 뭐지 알 수 없다. 이유 아시는 분 알려주세요 ㅠㅠ
그래서 어떻게 해야할 지 모르겠어서 3일은 여기저기 다 뒤지고 고민한 것 같다.
그래서 내가 생각해 낸 두번째 방법은 !
api를 개발할 때에는 h2 데이터베이스 전용 함수를 이용하고, 배포할 때에는 Mysql 전용 함수로 사용하는 방법이다.
👉🏻 H2/Mysql 문법 비교
// # 요일별 조회
@Override
public List<FindByDayResponse> findByDay(Long memberId){
// todo : 사용할 데이터베이스 문법으로 변경
/* mysql */
StringTemplate toDay = stringTemplate("DAYOFWEEK({0})", board.createdAt);
/* h2 */
// StringTemplate toDay = stringTemplate("DAY_OF_WEEK({0})", board.createdAt);
return queryFactory
.select(Projections.fields(FindByDayResponse.class,
toDay.when("1").then("sun")
.when("2").then("mon")
.when("3").then("tue")
.when("4").then("wed")
.when("5").then("thr")
.when("6").then("fri")
.when("7").then("sat")
.otherwise("기타").max().as("dayOfWeek"),
toDay.count().as("countDay")
))
.from(board)
.where(board.member.id.eq(memberId))
.groupBy(toDay)
.distinct()
.fetch();
}
바로 이렇게 말이다 ..
sql function이 존재하지 않는다는 에러를 보지 않고 드디어 api를 만들 수 있어 행복했지만
이 방식은 로컬에서 실행하는 경우와 배포 시에 주석 처리를 계속 바꾸어야 하기 때문에 번거롭고,
특정 데이터베이스에 종속되지 않는 것이 좋을 것 같아 나는 또 다시 다른 방법을 찾아보기로 했다.💨
2️⃣ Querydsl Expressions 이용하기
Querydsl에서 제공하는 Expressions을 사용해보았다.
Expressions 클래스는 동적인 표현식 생성을 위한 정적 팩토리 클래스이다.
👉🏻 사용법 클릭
public List<FindByDayResponse> findByDay(Long memberId){
NumberExpression<Integer> toDay = Expressions.numberOperation(Integer.class, Ops.DateTimeOps.DAY_OF_WEEK, board.createdAt);
return queryFactory
.select(Projections.fields(FindByDayResponse.class,
toDay.when(1).then("sun")
.when(2).then("mon")
.when(3).then("tue")
.when(4).then("wed")
.when(5).then("thr")
.when(6).then("fri")
.when(7).then("sat")
.otherwise("기타").as("dayOfWeek"),
toDay.count().as("countDay")
))
.from(board)
.where(board.member.id.eq(memberId))
.groupBy(toDay)
.orderBy(toDay.asc())
.distinct()
.fetch();
}
그렇게 특정 데이터베이스에 종속되지 않고
querydsl에서 제공하는 라이브러리를 통해 다양한 date format을 할 수 있었다.
특정 환경에서만 실행 가능한 쿼리가 아니라 어느 환경에서나 실행할 수 있다는 점이 엄청 큰 장점인 것 같다.
그리고 코드도 복잡하지도 않고, 너무 좋은 녀석 인 것 같다.
Expressions에는 정말 다양한 표현식이 있는 것 같다. 한번 쭈우욱 공부하고 나중에 써먹어야지 🥳
👉🏻 다양한 Expressions 사용법
+
이 녀석은 꿋꿋이 실행되지 않았다.
에러 코드를 보았을 때 해당 표현식은 데이터베이스의 date 함수를 호출하는 것 같았다.
'Server > Spring boot' 카테고리의 다른 글
[Spring boot] security + Oauth2로 구글 로그인 구현하기 - OAuth 서비스 등록 (0) | 2023.04.29 |
---|---|
docker-compose 로 Spring boot + MariaDB 개발 환경 구축하기 (0) | 2023.03.16 |
Spring Boot + AWS S3를 이용해 파일 업로드/삭제하기 (0) | 2023.01.26 |
[Swagger 3] Swagger2에서 달라진 것들 (0) | 2022.11.11 |
인텔리제이로 스프링 부트 시작하기 (0) | 2022.08.29 |