From cfa1f871e5ebe6051f6df1ce016089452466b537 Mon Sep 17 00:00:00 2001 From: byeolhaha Date: Sat, 2 Dec 2023 14:07:38 +0900 Subject: [PATCH] =?UTF-8?q?Feat=20:.=20=EB=AC=B4=ED=95=9C=EC=8A=A4?= =?UTF-8?q?=ED=81=AC=EB=A1=A4=20=EB=B0=A9=EC=8B=9D=20=ED=95=84=ED=84=B0=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EC=BF=BC=EB=A6=AC=20=EA=B5=AC=ED=98=84=20?= =?UTF-8?q?=20=EB=B0=8F=20=EC=A1=B0=EA=B1=B4=20=EC=BF=BC=EB=A6=AC=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=EB=B6=84=EB=A6=AC=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../academy/AcademyQueryRepository.java | 12 +- .../academy/AcademyQueryRepositoryImpl.java | 236 +++++++++++++----- .../repository/academy/AcademyRepository.java | 6 + .../dto/AcademiesByFilterWithScroll.java | 19 ++ .../dto/AcademyByFilterWithScroll.java | 13 + .../dto/AcademyByLocationWithScroll.java | 13 + 6 files changed, 232 insertions(+), 67 deletions(-) create mode 100644 src/main/java/org/guzzing/studayserver/domain/academy/repository/dto/AcademiesByFilterWithScroll.java create mode 100644 src/main/java/org/guzzing/studayserver/domain/academy/repository/dto/AcademyByFilterWithScroll.java create mode 100644 src/main/java/org/guzzing/studayserver/domain/academy/repository/dto/AcademyByLocationWithScroll.java diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyQueryRepository.java b/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyQueryRepository.java index 893671aa..24b77a46 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyQueryRepository.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyQueryRepository.java @@ -1,10 +1,8 @@ package org.guzzing.studayserver.domain.academy.repository.academy; import java.util.List; -import org.guzzing.studayserver.domain.academy.repository.dto.AcademiesByLocation; -import org.guzzing.studayserver.domain.academy.repository.dto.AcademiesByLocationWithScroll; -import org.guzzing.studayserver.domain.academy.repository.dto.AcademyByFiltering; -import org.guzzing.studayserver.domain.academy.repository.dto.AcademyFilterCondition; + +import org.guzzing.studayserver.domain.academy.repository.dto.*; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; @@ -19,4 +17,10 @@ AcademiesByLocationWithScroll findAcademiesByLocation( Long memberId, int pageNumber, int pageSize); + + AcademiesByFilterWithScroll filterAcademies( + AcademyFilterCondition academyFilterCondition, + Long memberId, + int pageNumber, + int pageSize); } diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyQueryRepositoryImpl.java b/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyQueryRepositoryImpl.java index 31e65ccf..f3a50852 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyQueryRepositoryImpl.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyQueryRepositoryImpl.java @@ -2,8 +2,11 @@ import jakarta.persistence.EntityManager; import jakarta.persistence.Query; + import java.util.List; + import org.guzzing.studayserver.domain.academy.repository.dto.*; +import org.hibernate.query.NativeQuery; import org.hibernate.transform.ResultTransformer; import org.hibernate.type.StandardBasicTypes; @@ -18,16 +21,24 @@ public AcademyQueryRepositoryImpl(EntityManager em) { public List findAcademiesByLocation(String pointFormat, Long memberId) { String nativeQuery = """ - SELECT a.id AS academyId, a.academy_name AS academyName, a.phone_number AS phoneNumber, a.full_address AS fullAddress, - a.latitude AS latitude , a.longitude AS longitude, a.shuttle AS shuttleAvailable, - (CASE WHEN l.academy_id IS NOT NULL THEN true ELSE false END) AS isLiked, - ac.category_id as categoryId - FROM academies AS a - LEFT JOIN academy_categories as ac - ON a.id = ac.academy_id - LEFT JOIN likes AS l - ON a.id = l.academy_id AND l.member_id = %s - WHERE MBRContains(ST_LINESTRINGFROMTEXT(%s), a.point)=1"""; + SELECT + a.id AS academyId, + a.academy_name AS academyName, + a.phone_number AS phoneNumber, + a.full_address AS fullAddress, + a.latitude AS latitude , + a.longitude AS longitude, + a.shuttle AS shuttleAvailable, + (CASE WHEN l.academy_id IS NOT NULL THEN true ELSE false END) AS isLiked, + ac.category_id as categoryId + FROM + academies AS a + LEFT JOIN + academy_categories as ac ON a.id = ac.academy_id + LEFT JOIN + likes AS l ON a.id = l.academy_id AND l.member_id = %s + WHERE + MBRContains(ST_LINESTRINGFROMTEXT(%s), a.point)=1"""; String formattedQuery = String.format(nativeQuery, memberId, pointFormat); @@ -74,25 +85,27 @@ public List transformList(List collection) { public List filterAcademies(AcademyFilterCondition academyFilterCondition, Long memberId) { String nativeQuery = """ - SELECT a.id AS academyId, a.academy_name AS academyName, a.full_address AS fullAddress, - a.phone_number AS phoneNumber, a.latitude, a.longitude, a.shuttle AS shuttleAvailable, - ac.category_id as categoryId, - (CASE WHEN l.academy_id IS NOT NULL THEN true ELSE false END) AS isLiked - FROM academy_categories as ac - LEFT JOIN academies AS a ON ac.academy_id = a.id - LEFT JOIN likes AS l ON a.id = l.academy_id AND l.member_id = %s - WHERE MBRContains(ST_LINESTRINGFROMTEXT(%s), a.point)=1"""; + SELECT + a.id AS academyId, + a.academy_name AS academyName, + a.full_address AS fullAddress, + a.phone_number AS phoneNumber, + a.latitude, + a.longitude, + a.shuttle AS shuttleAvailable, + ac.category_id as categoryId, + (CASE WHEN l.academy_id IS NOT NULL THEN true ELSE false END) AS isLiked + FROM + academy_categories as ac + LEFT JOIN + academies AS a ON ac.academy_id = a.id + LEFT JOIN + likes AS l ON a.id = l.academy_id AND l.member_id = %s + WHERE + MBRContains(ST_LINESTRINGFROMTEXT(%s), a.point)=1"""; String formattedQuery = String.format(nativeQuery, memberId, academyFilterCondition.pointFormat()); - - if (academyFilterCondition.categories() != null && !academyFilterCondition.categories().isEmpty()) { - formattedQuery += " AND ac.category_id IN " + academyFilterCondition.categories(); - } - - if (academyFilterCondition.desiredMinAmount() != null && academyFilterCondition.desiredMaxAmount() != null) { - formattedQuery += " AND max_education_fee BETWEEN " + academyFilterCondition.desiredMinAmount() + " AND " - + academyFilterCondition.desiredMaxAmount(); - } + formattedQuery = addWhereConditionsWithFilter(formattedQuery, academyFilterCondition); Query query = em.createNativeQuery(formattedQuery); @@ -139,33 +152,91 @@ public AcademiesByLocationWithScroll findAcademiesByLocation( int pageSize) { String nativeQuery = """ - SELECT - a.id AS academyId, - a.academy_name AS academyName, - a.phone_number AS phoneNumber, - a.full_address AS fullAddress, - a.latitude AS latitude, - a.longitude AS longitude, - a.shuttle AS shuttleAvailable, - (CASE WHEN l.academy_id IS NOT NULL THEN true ELSE false END) AS isLiked - FROM - academies AS a - LEFT JOIN - likes AS l ON a.id = l.academy_id AND l.member_id = %s - WHERE - MBRContains(ST_LINESTRINGFROMTEXT(%s), a.point) = 1 - ORDER BY a.academy_name - LIMIT %s OFFSET %s"""; - - int offset = pageNumber * pageSize; + SELECT + a.id AS academyId, + a.academy_name AS academyName, + a.phone_number AS phoneNumber, + a.full_address AS fullAddress, + a.latitude AS latitude, + a.longitude AS longitude, + a.shuttle AS shuttleAvailable, + (CASE WHEN l.academy_id IS NOT NULL THEN true ELSE false END) AS isLiked + FROM + academies AS a + LEFT JOIN + likes AS l ON a.id = l.academy_id AND l.member_id = %s + WHERE + MBRContains(ST_LINESTRINGFROMTEXT(%s), a.point) = 1 + ORDER BY a.academy_name"""; - String formattedQuery = String.format(nativeQuery, memberId, pointFormat, pageSize, offset); + String formattedQuery = String.format(nativeQuery, memberId, pointFormat); + formattedQuery = makeScroll(pageNumber, pageSize, formattedQuery); Query emNativeQuery = em.createNativeQuery( formattedQuery); List academiesByLocation = emNativeQuery.unwrap(org.hibernate.query.NativeQuery.class) + .addScalar("academyId", StandardBasicTypes.LONG) + .addScalar("academyName", StandardBasicTypes.STRING) + .addScalar("fullAddress", StandardBasicTypes.STRING) + .addScalar("phoneNumber", StandardBasicTypes.STRING) + .addScalar("latitude", StandardBasicTypes.DOUBLE) + .addScalar("longitude", StandardBasicTypes.DOUBLE) + .addScalar("shuttleAvailable", StandardBasicTypes.STRING) + .addScalar("isLiked", StandardBasicTypes.BOOLEAN) + .setResultTransformer((tuple, aliases) -> new AcademyByLocationWithScroll( + (Long) tuple[0], + (String) tuple[1], + (String) tuple[2], + (String) tuple[3], + (Double) tuple[4], + (Double) tuple[5], + (String) tuple[6], + (boolean) tuple[7] + )) + .getResultList(); + + return AcademiesByLocationWithScroll.of( + academiesByLocation, + isHasNest(academiesByLocation.size(), pageSize) + ); + } + + public AcademiesByFilterWithScroll filterAcademies( + AcademyFilterCondition academyFilterCondition, + Long memberId, + int pageNumber, + int pageSize) { + String nativeQuery = """ + SELECT + a.id AS academyId, + a.academy_name AS academyName, + a.full_address AS fullAddress, + a.phone_number AS phoneNumber, + a.latitude, a.longitude, + a.shuttle AS shuttleAvailable, + (CASE WHEN l.academy_id IS NOT NULL THEN true ELSE false END) AS isLiked + FROM + academy_categories as ac + LEFT JOIN + academies AS a ON ac.academy_id = a.id + LEFT JOIN + likes AS l ON a.id = l.academy_id AND l.member_id = %s + WHERE + MBRContains(ST_LINESTRINGFROMTEXT(%s), a.point)=1"""; + + + String formattedQuery = String.format( + nativeQuery, + memberId, + academyFilterCondition.pointFormat()); + formattedQuery = addWhereConditionsWithFilter(formattedQuery, academyFilterCondition); + formattedQuery = makeScroll(pageNumber, pageSize, formattedQuery); + + Query query = em.createNativeQuery(formattedQuery); + + List academyByFilter = query.unwrap(NativeQuery.class) .addScalar("academyId", StandardBasicTypes.LONG) .addScalar("academyName", StandardBasicTypes.STRING) .addScalar("fullAddress", StandardBasicTypes.STRING) @@ -174,26 +245,65 @@ public AcademiesByLocationWithScroll findAcademiesByLocation( .addScalar("longitude", StandardBasicTypes.DOUBLE) .addScalar("shuttleAvailable", StandardBasicTypes.STRING) .addScalar("isLiked", StandardBasicTypes.BOOLEAN) - .setResultTransformer((tuple, aliases) -> new AcademyByLocationWithScroll( - (Long) tuple[0], - (String) tuple[1], - (String) tuple[2], - (String) tuple[3], - (Double) tuple[4], - (Double) tuple[5], - (String) tuple[6], - (boolean) tuple[7] - )) + .setResultTransformer( + new ResultTransformer() { + @Override + public Object transformTuple(Object[] tuple, String[] aliases) { + return new AcademyByFilterWithScroll( + (Long) tuple[0], + (String) tuple[1], + (String) tuple[2], + (String) tuple[3], + (Double) tuple[4], + (Double) tuple[5], + (String) tuple[6], + (boolean) tuple[7] + ); + } + + @Override + public List transformList(List collection) { + return collection; + } + } + ) .getResultList(); - return AcademiesByLocationWithScroll.of( - academiesByLocation, - isHasNest(academiesByLocation,pageSize) + return AcademiesByFilterWithScroll.of( + academyByFilter, + isHasNest(academyByFilter.size(), pageSize) ); } - private boolean isHasNest(List academiesByLocation, int pageSize) { - return academiesByLocation.size() == pageSize; + private boolean isHasNest(int resultSize, int pageSize) { + return resultSize == pageSize; + } + + private String addWhereConditionsWithFilter(String formattedQuery, AcademyFilterCondition academyFilterCondition) { + formattedQuery += whereInCategories(academyFilterCondition); + formattedQuery += whereBetweenEducationFee(academyFilterCondition); + return formattedQuery; + } + + private String whereInCategories(AcademyFilterCondition academyFilterCondition) { + if (academyFilterCondition.categories() != null && !academyFilterCondition.categories().isEmpty()) { + return " AND ac.category_id IN " + academyFilterCondition.categories(); + } + return ""; + } + + private String whereBetweenEducationFee(AcademyFilterCondition academyFilterCondition) { + if (academyFilterCondition.desiredMinAmount() != null && academyFilterCondition.desiredMaxAmount() != null) { + return " AND max_education_fee BETWEEN " + academyFilterCondition.desiredMinAmount() + " AND " + + academyFilterCondition.desiredMaxAmount(); + } + return ""; + } + + private String makeScroll(int pageNumber, int pageSize, String formattedQuery) { + int offset = pageNumber * pageSize; + formattedQuery += " ORDER BY a.academy_name LIMIT " + pageSize + " OFFSET " + offset; + return formattedQuery; } - + } diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyRepository.java b/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyRepository.java index 6117474e..ff57bf49 100644 --- a/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyRepository.java +++ b/src/main/java/org/guzzing/studayserver/domain/academy/repository/academy/AcademyRepository.java @@ -32,4 +32,10 @@ AcademiesByLocationWithScroll findAcademiesByLocation( Long memberId, int pageNumber, int pageSize); + + AcademiesByFilterWithScroll filterAcademies( + AcademyFilterCondition academyFilterCondition, + Long memberId, + int pageNumber, + int pageSize); } diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/repository/dto/AcademiesByFilterWithScroll.java b/src/main/java/org/guzzing/studayserver/domain/academy/repository/dto/AcademiesByFilterWithScroll.java new file mode 100644 index 00000000..8f06c0f6 --- /dev/null +++ b/src/main/java/org/guzzing/studayserver/domain/academy/repository/dto/AcademiesByFilterWithScroll.java @@ -0,0 +1,19 @@ +package org.guzzing.studayserver.domain.academy.repository.dto; + +import java.util.List; + +public record AcademiesByFilterWithScroll ( + List academiesByLocation, + boolean hasNext +) { + public static AcademiesByFilterWithScroll of( + List academiesByLocation, + boolean hasNext + ) { + return new AcademiesByFilterWithScroll( + academiesByLocation, + hasNext + ); + } + +} diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/repository/dto/AcademyByFilterWithScroll.java b/src/main/java/org/guzzing/studayserver/domain/academy/repository/dto/AcademyByFilterWithScroll.java new file mode 100644 index 00000000..22fcd05b --- /dev/null +++ b/src/main/java/org/guzzing/studayserver/domain/academy/repository/dto/AcademyByFilterWithScroll.java @@ -0,0 +1,13 @@ +package org.guzzing.studayserver.domain.academy.repository.dto; + +public record AcademyByFilterWithScroll ( + Long academyId, + String academyName, + String fullAddress, + String phoneNumber, + Double latitude, + Double longitude, + String shuttleAvailable, + boolean isLiked +) { +} diff --git a/src/main/java/org/guzzing/studayserver/domain/academy/repository/dto/AcademyByLocationWithScroll.java b/src/main/java/org/guzzing/studayserver/domain/academy/repository/dto/AcademyByLocationWithScroll.java new file mode 100644 index 00000000..03a28dca --- /dev/null +++ b/src/main/java/org/guzzing/studayserver/domain/academy/repository/dto/AcademyByLocationWithScroll.java @@ -0,0 +1,13 @@ +package org.guzzing.studayserver.domain.academy.repository.dto; + +public record AcademyByLocationWithScroll( + Long academyId, + String academyName, + String fullAddress, + String phoneNumber, + Double latitude, + Double longitude, + String shuttleAvailable, + boolean isLiked +) { +}