부트캠프에서 팀 프로젝트를 진행하면서 QueryDsl을 사용하게 되었다.
마지막 기능 구현 부분인 모임의 일정 기능 구현을 진행하다가
유저가 모임 일정을 생성하려는 경우 일 별로 유저가 모임을 참여했거나, 모임을 생성한 시간들을 데이터 베이스에서 가져와서 일정이 겹치는 경우 생성을 막아줘야하는 부분을 구현해야 할 것 같아서 QueryDsl에서 MySQL Function을 사용 해야 할 일이 생겨버렸다..
처음에는 당연히 MySQL의 함수를 쓸 수 있을거 같아서 바로 쓴 결과는 실패였다.
No data type for node: org.hibernate.hql.internal.ast.tree.MethodNode
위 내용으로 구글링을 한 결과 dialect를 extends한 CustomDialect를 만들어 줘야 했다.
// CustomMySQL8InnoDBDialect
public class CustomMySQL8InnoDBDialect extends MySQL8Dialect {
public CustomMySQL8InnoDBDialect() {
super();
this.registerFunction("DATE_FORMAT", new StandardSQLFunction("DATE_FORMAT", StandardBasicTypes.STRING));
this.registerFunction("TIMESTAMPDIFF", new StandardSQLFunction("TIMESTAMPDIFF", StandardBasicTypes.LONG));
}
}
// yml
jpa:
open-in-view: false
properties:
hibernate:
format_sql: true
default_batch_fetch_size: 1000
jdbc.batch_size: 1000
dialect: com.workduo.configuration.querydsl.CustomMySQL8InnoDBDialect // 이 부분을 custom dialect으로 변경
database: mysql
여기서 주의 할 점은 함수를 써서 반환 받을 타입을 StandardBasicTypes.반환타입 으로 잘 정의해줘야 한다
밑 코드에서 dto의 term이 Long 타입인데 TIMESTAMPDIFF를 StandardBasicType.String 으로 지정을했더니
argument type mismatch 라는 예외가 터진다.
// DTO
@Getter
@Setter
@NoArgsConstructor
@Builder
public class MeetingInquireDto {
private String time;
private Long term;
@QueryProjection
public MeetingInquireDto(String time, Long term) {
this.time = time;
this.term = term;
}
}
// query repository
@Repository
@RequiredArgsConstructor
public class GroupMeetingQueryRepositoryImpl implements GroupMeetingQueryRepository {
private final JPAQueryFactory jpaQueryFactory;
@Override
public List<MeetingInquireDto> meetingInquireList(Long memberId, LocalDateTime startDate, LocalDateTime endDate) {
return jpaQueryFactory
.select(
new QMeetingInquireDto(
startTime(groupMeeting.meetingStartDate),
minuteDiff(
groupMeeting.meetingStartDate,
groupMeeting.meetingEndDate
)
)
)
.from(groupMeeting)
.where(inquireMeeting(memberId, startDate, endDate))
.fetch();
}
private StringTemplate startTime(Expression<? extends LocalDateTime> startDate) {
return Expressions.stringTemplate(
"DATE_FORMAT({0}, {1})",
groupMeeting.meetingStartDate,
"%H:%i"
);
}
private NumberExpression<Long> minuteDiff(
Expression<? extends LocalDateTime> startDate,
Expression<? extends LocalDateTime> endDate) {
return MathExpressions.round(
Expressions.numberTemplate(
Long.class,
"TIMESTAMPDIFF(MINUTE, {0}, {1})",
startDate,
endDate
)
,-1
);
}
private BooleanExpression inquireMeeting(
Long memberId,
LocalDateTime startDate,
LocalDateTime endDate) {
return groupMeeting.id.in(
select(groupMeetingParticipant.groupMeeting.id)
.from(groupMeetingParticipant)
.join(groupMeetingParticipant.member, member)
.where(
groupMeetingParticipant.member.id.eq(memberId),
groupMeeting.meetingStartDate.between(
startDate,
endDate
)
)
);
}
}
// test code
@Test
@DisplayName("meetingInquireList")
@Transactional
@Rollback(value = false)
public void meetingInquireList() throws Exception {
// given
Long memberId = 3L;
LocalDateTime startDate = LocalDateTime.of(2022, 9, 26, 0, 0, 0);
LocalDateTime endDate = LocalDateTime.of(2022, 9, 26, 23, 59, 59, 999999);
// when
List<MeetingInquireDto> meetingInquireDtos = groupMeetingQueryRepository.meetingInquireList(memberId, startDate, endDate);
// then
for (MeetingInquireDto meetingInquireDto : meetingInquireDtos) {
System.out.println(meetingInquireDto.getTime() + " " + meetingInquireDto.getTerm());
}
}
실행 결과를 보니 원하는 대로 쿼리가 잘 나가고 결과도 잘 나온다.
2022-09-26의 유저3의 일정
{
"date": "2022-09-26",
"times": [
{
"time": "00:00",
"disabled": true
},
{
"time": "01:00",
"disabled": false
},
{
"time": "02:00",
"disabled": false
},
{
"time": "03:00",
"disabled": true
},
{
"time": "04:00",
"disabled": false
},
{
"time": "05:00",
"disabled": false
},
{
"time": "06:00",
"disabled": false
},
{
"time": "07:00",
"disabled": false
},
{
"time": "08:00",
"disabled": false
},
{
"time": "09:00",
"disabled": false
},
{
"time": "10:00",
"disabled": false
},
{
"time": "11:00",
"disabled": false
},
{
"time": "12:00",
"disabled": false
},
{
"time": "13:00",
"disabled": false
},
{
"time": "14:00",
"disabled": false
},
{
"time": "15:00",
"disabled": false
},
{
"time": "16:00",
"disabled": false
},
{
"time": "17:00",
"disabled": false
},
{
"time": "18:00",
"disabled": false
},
{
"time": "19:00",
"disabled": false
},
{
"time": "20:00",
"disabled": true
},
{
"time": "21:00",
"disabled": true
},
{
"time": "22:00",
"disabled": true
},
{
"time": "23:00",
"disabled": false
}
]
}
유저3은 00:00 ~ 01:00, 03:00 ~ 04:00, 20:00 ~ 23:00 이렇게 3개의 일정이 있으므로 해당 시간에는 disabled를 true로 보내줌으로써 해당 시간에 모임 생성을 못 하도록 막을 수 있다.
'BackEnd > QueryDsl JPA' 카테고리의 다른 글
QueryDsl에서 exists과 동일한 성능을 낼 수 있는 fetchOne (0) | 2022.09.27 |
---|