diff --git a/build.gradle b/build.gradle index 2024341..22ba0da 100644 --- a/build.gradle +++ b/build.gradle @@ -33,8 +33,34 @@ dependencies { testImplementation 'org.springframework.boot:spring-boot-starter-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' testImplementation 'com.h2database:h2' + + // QueryDSL : OpenFeign + implementation "io.github.openfeign.querydsl:querydsl-jpa:7.0" + implementation "io.github.openfeign.querydsl:querydsl-core:7.0" + annotationProcessor "io.github.openfeign.querydsl:querydsl-apt:7.0:jpa" + annotationProcessor "jakarta.persistence:jakarta.persistence-api" + annotationProcessor "jakarta.annotation:jakarta.annotation-api" } tasks.named('test') { useJUnitPlatform() } + +// QueryDSL 관련 설정 +// generated/querydsl 폴더 생성 & 삽입 +def querydslDir = layout.buildDirectory.dir("generated/querydsl").get().asFile + +// 소스 세트에 생성 경로 추가 (구체적인 경로 지정) +sourceSets { + main.java.srcDirs += [ querydslDir ] +} + +// 컴파일 시 생성 경로 지정 +tasks.withType(JavaCompile).configureEach { + options.generatedSourceOutputDirectory.set(querydslDir) +} + +// clean 태스크에 생성 폴더 삭제 로직 추가 +clean.doLast { + file(querydslDir).deleteDir() +} \ No newline at end of file diff --git a/src/main/java/com/example/umc9th/domain/review/controller/ReviewController.java b/src/main/java/com/example/umc9th/domain/review/controller/ReviewController.java new file mode 100644 index 0000000..b94c011 --- /dev/null +++ b/src/main/java/com/example/umc9th/domain/review/controller/ReviewController.java @@ -0,0 +1,31 @@ +package com.example.umc9th.domain.review.controller; + +import com.example.umc9th.domain.review.entity.Review; +import com.example.umc9th.domain.review.service.ReviewQueryService; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/reviews") +public class ReviewController { + private final ReviewQueryService reviewQueryService; + + // /api/reviews/me?memberId=7 + // /api/reviews/me?memberId=7&type=restaurant&query=반이학생마라탕마라반 + // /api/reviews/me?memberId=7&type=rating&query=4 + // /api/reviews/me?memberId=7&type=both&query=반이학생마라탕마라반&4 + @GetMapping("/me") + public List myReviews( + @RequestParam Long memberId, + @RequestParam(required = false) String type, + @RequestParam(required = false) String query + ) { + return reviewQueryService.searchMyReviews(memberId, type, query); + } +} diff --git a/src/main/java/com/example/umc9th/domain/review/repository/ReviewQueryDsl.java b/src/main/java/com/example/umc9th/domain/review/repository/ReviewQueryDsl.java new file mode 100644 index 0000000..af1dea2 --- /dev/null +++ b/src/main/java/com/example/umc9th/domain/review/repository/ReviewQueryDsl.java @@ -0,0 +1,11 @@ +package com.example.umc9th.domain.review.repository; + +import com.example.umc9th.domain.review.entity.Review; +import com.querydsl.core.types.Predicate; + +import java.util.List; + +public interface ReviewQueryDsl { + // 리뷰 조회 API + public List searchReview(Predicate predicate); +} diff --git a/src/main/java/com/example/umc9th/domain/review/repository/ReviewQueryDslImpl.java b/src/main/java/com/example/umc9th/domain/review/repository/ReviewQueryDslImpl.java new file mode 100644 index 0000000..f5dbb61 --- /dev/null +++ b/src/main/java/com/example/umc9th/domain/review/repository/ReviewQueryDslImpl.java @@ -0,0 +1,33 @@ +package com.example.umc9th.domain.review.repository; + +import com.example.umc9th.domain.review.entity.QReply; +import com.example.umc9th.domain.review.entity.QReview; +import com.example.umc9th.domain.review.entity.Review; +import com.querydsl.core.types.Predicate; +import com.querydsl.jpa.impl.JPAQueryFactory; +import jakarta.persistence.EntityManager; +import lombok.RequiredArgsConstructor; + +import java.util.List; + +@RequiredArgsConstructor +public class ReviewQueryDslImpl implements ReviewQueryDsl { + private final EntityManager em; + + // 내가 작성한 리뷰 조회 API(가게별, 별점대별) + @Override + public List searchReview(Predicate predicate) { + JPAQueryFactory queryFactory = new JPAQueryFactory(em); + + // Q 클래스 선언 + QReview review = QReview.review; + QReply reply = QReply.reply; + + //review와 reply left outer join + return queryFactory + .selectFrom(review) + .leftJoin(review.reply, reply).fetchJoin() + .where(predicate) + .fetch(); + } +} diff --git a/src/main/java/com/example/umc9th/domain/review/repository/ReviewRepository.java b/src/main/java/com/example/umc9th/domain/review/repository/ReviewRepository.java index 37161b0..64d965b 100644 --- a/src/main/java/com/example/umc9th/domain/review/repository/ReviewRepository.java +++ b/src/main/java/com/example/umc9th/domain/review/repository/ReviewRepository.java @@ -8,7 +8,7 @@ import java.util.List; -public interface ReviewRepository extends JpaRepository { +public interface ReviewRepository extends JpaRepository, ReviewQueryDsl { // 특정 식당의 리뷰 조회 List findByRestaurant(Restaurant restaurant); diff --git a/src/main/java/com/example/umc9th/domain/review/service/ReviewQueryService.java b/src/main/java/com/example/umc9th/domain/review/service/ReviewQueryService.java new file mode 100644 index 0000000..00505f8 --- /dev/null +++ b/src/main/java/com/example/umc9th/domain/review/service/ReviewQueryService.java @@ -0,0 +1,75 @@ +package com.example.umc9th.domain.review.service; + +import com.example.umc9th.domain.review.entity.QReview; +import com.example.umc9th.domain.review.entity.Review; +import com.example.umc9th.domain.review.repository.ReviewRepository; +import com.querydsl.core.BooleanBuilder; +import jakarta.transaction.Transactional; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +@RequiredArgsConstructor +@Transactional +public class ReviewQueryService { + private final ReviewRepository reviewRepository; + + + public List searchMyReviews(Long memberId, String type, String query){ + QReview review = QReview.review; + + // BooleanBuilder 선언 + BooleanBuilder builder = new BooleanBuilder(); + + // BooleanBuilder 사용 + + // 1) 내가 작성한 리뷰 + builder.and(review.member.id.eq(memberId)); + + // 동적 쿼리 : 조회 조건 + if (type.equals("restaurant")) { + builder.and(review.restaurant.restaurantName.contains(query)); + } + if (type.equals("rating")) { + double base = Double.parseDouble(query.trim()); + if (base >= 5.0) { + // 정확히 5.0만 + builder.and(review.rating.eq(5.0)); + } else if (base >= 0.0) { + // n점대 + double lower = base; + double upper = Math.min(5.0, base + 1.0); + builder.and(review.rating.goe(lower)); + builder.and(review.rating.lt(upper)); + } + + } + if(type.equals("both")) { + String firstQuery = query.split("&")[0]; + String secondQuery = query.split("&")[1]; + + builder.and(review.restaurant.restaurantName.contains(query)); + + double base = Double.parseDouble(query.trim()); + if (base >= 5.0) { + // 정확히 5.0만 + builder.and(review.rating.eq(5.0)); + } else if (base >= 0.0) { + // n점대 + double lower = base; + double upper = Math.min(5.0, base + 1.0); + builder.and(review.rating.goe(lower)); + builder.and(review.rating.lt(upper)); + } + + } + + + List reviewList = reviewRepository.searchReview(builder); + + return reviewList; + } + +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 7a9401f..92a91fc 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -16,4 +16,9 @@ spring: ddl-auto: update # ?????? ?? ? ?????? ???? ??? ?? properties: hibernate: - format_sql: true # ???? SQL ??? ?? ?? ??? \ No newline at end of file + format_sql: true # ???? SQL ??? ?? ?? ??? + +logging: + level: + org.hibernate.SQL: debug + org.hibernate.orm.jdbc.bind: trace \ No newline at end of file