Skip to content

Commit

Permalink
Merge pull request #20 from djkeh/feature/#14-jpa-advanced
Browse files Browse the repository at this point in the history
복잡한 쿼리의 작성과 응용
  • Loading branch information
djkeh authored Oct 4, 2021
2 parents 3bb32c4 + 0226e14 commit c6ced79
Show file tree
Hide file tree
Showing 17 changed files with 521 additions and 30 deletions.
23 changes: 13 additions & 10 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ repositories {
mavenCentral()
}

project.ext {
querydslVersion = dependencyManagement.importedProperties['querydsl.version']
}

dependencies {
developmentOnly 'org.springframework.boot:spring-boot-devtools'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
Expand All @@ -33,31 +37,30 @@ dependencies {
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
testImplementation 'org.springframework.boot:spring-boot-starter-test'

// queryDSL 설정
// Querydsl
implementation "com.querydsl:querydsl-jpa"
implementation "com.querydsl:querydsl-core"
implementation "com.querydsl:querydsl-collections"
annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jpa" // querydsl JPAAnnotationProcessor 사용 지정
annotationProcessor "com.querydsl:querydsl-apt:${project.querydslVersion}:jpa" // querydsl JPAAnnotationProcessor 사용 지정
annotationProcessor "jakarta.annotation:jakarta.annotation-api" // java.lang.NoClassDefFoundError (javax.annotation.Generated) 발생 대응
}

test {
useJUnitPlatform()
}

// querydsl 적용
def generated='src/main/generated'
//// Querydsl 설정부
def generated = 'src/main/generated'

// querydsl QClass 파일 생성 위치를 지정
tasks.withType(JavaCompile) {
options.getGeneratedSourceOutputDirectory().set(file(generated))
}

// java source set 에 querydsl QClass 위치 추가
sourceSets {
main.java.srcDirs += [ generated ]
}

// querydsl QClass 파일 위치를 잡아주는 설정
tasks.withType(JavaCompile) {
options.getGeneratedSourceOutputDirectory().set(file(generated))
}

// gradle clean 시에 QClass 디렉토리 삭제
clean {
delete file(generated)
Expand Down
33 changes: 33 additions & 0 deletions src/main/java/com/uno/getinline/controller/EventController.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,32 @@

import com.querydsl.core.types.Predicate;
import com.uno.getinline.constant.ErrorCode;
import com.uno.getinline.constant.EventStatus;
import com.uno.getinline.domain.Event;
import com.uno.getinline.dto.EventResponse;
import com.uno.getinline.dto.EventViewResponse;
import com.uno.getinline.exception.GeneralException;
import com.uno.getinline.service.EventService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.querydsl.binding.QuerydslPredicate;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Controller;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import javax.validation.constraints.Size;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RequiredArgsConstructor
@Validated
@RequestMapping("/events")
@Controller
public class EventController {
Expand All @@ -38,6 +47,30 @@ public ModelAndView events(@QuerydslPredicate(root = Event.class) Predicate pred
return new ModelAndView("event/index", map);
}

@GetMapping("/custom")
public ModelAndView customEvents(
@Size(min = 2) String placeName,
@Size(min = 2) String eventName,
EventStatus eventStatus,
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime eventStartDatetime,
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime eventEndDatetime,
Pageable pageable
) {
Map<String, Object> map = new HashMap<>();
Page<EventViewResponse> events = eventService.getEventViewResponse(
placeName,
eventName,
eventStatus,
eventStartDatetime,
eventEndDatetime,
pageable
);

map.put("events", events);

return new ModelAndView("event/index", map);
}

@GetMapping("/{eventId}")
public ModelAndView eventDetail(@PathVariable Long eventId) {
Map<String, Object> map = new HashMap<>();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.uno.getinline.controller.api;

import com.uno.getinline.constant.EventStatus;
import com.uno.getinline.constant.PlaceType;
import com.uno.getinline.dto.ApiDataResponse;
import com.uno.getinline.dto.EventRequest;
import com.uno.getinline.dto.EventResponse;
import com.uno.getinline.dto.PlaceDto;
import com.uno.getinline.service.EventService;
import lombok.RequiredArgsConstructor;
import org.springframework.format.annotation.DateTimeFormat;
Expand Down Expand Up @@ -40,15 +42,27 @@ public ApiDataResponse<List<EventResponse>> getEvents(
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime eventStartDatetime,
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime eventEndDatetime
) {
List<EventResponse> eventResponses = eventService.getEvents(
placeId,
eventName,
eventStatus,
eventStartDatetime,
eventEndDatetime
).stream().map(EventResponse::from).toList();

return ApiDataResponse.of(eventResponses);
return ApiDataResponse.of(List.of(EventResponse.of(
1L,
PlaceDto.of(
1L,
PlaceType.SPORTS,
"배드민턴장",
"서울시 가나구 다라동",
"010-1111-2222",
0,
null,
LocalDateTime.now(),
LocalDateTime.now()
),
"오후 운동",
EventStatus.OPENED,
LocalDateTime.of(2021, 1, 1, 13, 0, 0),
LocalDateTime.of(2021, 1, 1, 16, 0, 0),
0,
24,
"마스크 꼭 착용하세요"
)));
}

@ResponseStatus(HttpStatus.CREATED)
Expand Down
69 changes: 69 additions & 0 deletions src/main/java/com/uno/getinline/dto/EventViewResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package com.uno.getinline.dto;

import com.uno.getinline.constant.EventStatus;

import java.time.LocalDateTime;

public record EventViewResponse(
Long id,
String placeName,
String eventName,
EventStatus eventStatus,
LocalDateTime eventStartDatetime,
LocalDateTime eventEndDatetime,
Integer currentNumberOfPeople,
Integer capacity,
String memo
) {
public EventViewResponse(Long id, String placeName, String eventName, EventStatus eventStatus, LocalDateTime eventStartDatetime, LocalDateTime eventEndDatetime, Integer currentNumberOfPeople, Integer capacity, String memo) {
this.id = id;
this.placeName = placeName;
this.eventName = eventName;
this.eventStatus = eventStatus;
this.eventStartDatetime = eventStartDatetime;
this.eventEndDatetime = eventEndDatetime;
this.currentNumberOfPeople = currentNumberOfPeople;
this.capacity = capacity;
this.memo = memo;
}

public static EventViewResponse of(
Long id,
String placeName,
String eventName,
EventStatus eventStatus,
LocalDateTime eventStartDatetime,
LocalDateTime eventEndDatetime,
Integer currentNumberOfPeople,
Integer capacity,
String memo
) {
return new EventViewResponse(
id,
placeName,
eventName,
eventStatus,
eventStartDatetime,
eventEndDatetime,
currentNumberOfPeople,
capacity,
memo
);
}

public static EventViewResponse from(EventDto eventDTO) {
if (eventDTO == null) { return null; }
return EventViewResponse.of(
eventDTO.id(),
eventDTO.placeDto().placeName(),
eventDTO.eventName(),
eventDTO.eventStatus(),
eventDTO.eventStartDatetime(),
eventDTO.eventEndDatetime(),
eventDTO.currentNumberOfPeople(),
eventDTO.capacity(),
eventDTO.memo()
);
}

}
10 changes: 10 additions & 0 deletions src/main/java/com/uno/getinline/exception/GeneralException.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,16 @@ public GeneralException(ErrorCode errorCode) {
this.errorCode = errorCode;
}

public GeneralException(ErrorCode errorCode, String message) {
super(errorCode.getMessage(message));
this.errorCode = errorCode;
}

public GeneralException(ErrorCode errorCode, String message, Throwable cause) {
super(errorCode.getMessage(message), cause);
this.errorCode = errorCode;
}

public GeneralException(ErrorCode errorCode, Throwable cause) {
super(errorCode.getMessage(cause), cause);
this.errorCode = errorCode;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@
import com.querydsl.core.types.dsl.StringExpression;
import com.uno.getinline.domain.Event;
import com.uno.getinline.domain.QEvent;
import com.uno.getinline.repository.querydsl.EventRepositoryCustom;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.data.querydsl.binding.QuerydslBinderCustomizer;
import org.springframework.data.querydsl.binding.QuerydslBindings;

public interface EventRepository extends
JpaRepository<Event, Long>,
EventRepositoryCustom,
QuerydslPredicateExecutor<Event>,
QuerydslBinderCustomizer<QEvent> {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.uno.getinline.repository.querydsl;

import com.uno.getinline.constant.EventStatus;
import com.uno.getinline.dto.EventViewResponse;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import java.time.LocalDateTime;

public interface EventRepositoryCustom {
Page<EventViewResponse> findEventViewPageBySearchParams(
String placeName,
String eventName,
EventStatus eventStatus,
LocalDateTime eventStartDatetime,
LocalDateTime eventEndDatetime,
Pageable pageable
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package com.uno.getinline.repository.querydsl;

import com.querydsl.core.types.Projections;
import com.querydsl.jpa.JPQLQuery;
import com.uno.getinline.constant.ErrorCode;
import com.uno.getinline.constant.EventStatus;
import com.uno.getinline.domain.Event;
import com.uno.getinline.domain.QEvent;
import com.uno.getinline.dto.EventViewResponse;
import com.uno.getinline.exception.GeneralException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;

public class EventRepositoryCustomImpl extends QuerydslRepositorySupport implements EventRepositoryCustom {

public EventRepositoryCustomImpl() {
super(Event.class);
}

@Override
public Page<EventViewResponse> findEventViewPageBySearchParams(
String placeName,
String eventName,
EventStatus eventStatus,
LocalDateTime eventStartDatetime,
LocalDateTime eventEndDatetime,
Pageable pageable
) {
QEvent event = QEvent.event;

JPQLQuery<EventViewResponse> query = from(event)
.select(Projections.constructor(
EventViewResponse.class,
event.id,
event.place.placeName,
event.eventName,
event.eventStatus,
event.eventStartDatetime,
event.eventEndDatetime,
event.currentNumberOfPeople,
event.capacity,
event.memo
));

if (placeName != null && !placeName.isBlank()) {
query.where(event.place.placeName.contains(placeName));
}
if (eventName != null && !eventName.isBlank()) {
query.where(event.eventName.contains(eventName));
}
if (eventStatus != null) {
query.where(event.eventStatus.eq(eventStatus));
}
if (eventStartDatetime != null) {
query.where(event.eventStartDatetime.goe(eventStartDatetime));
}
if (eventEndDatetime != null) {
query.where(event.eventEndDatetime.loe(eventEndDatetime));
}

List<EventViewResponse> events = Optional.ofNullable(getQuerydsl())
.orElseThrow(() -> new GeneralException(ErrorCode.DATA_ACCESS_ERROR, "Spring Data JPA 로부터 Querydsl 인스턴스를 가져올 수 없다."))
.applyPagination(pageable, query).fetch();

return new PageImpl<>(events, pageable, query.fetchCount());
}

}
Loading

0 comments on commit c6ced79

Please sign in to comment.