From 3a5ffe1cb2a2ee9f326ac75d37a95e35f74bdbb0 Mon Sep 17 00:00:00 2001 From: blaxsior Date: Thu, 22 Aug 2024 18:10:09 +0900 Subject: [PATCH 1/3] =?UTF-8?q?[feat]=20jpa=20specification=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20=EC=9C=A0=EC=A0=80=20?= =?UTF-8?q?=EA=B2=80=EC=83=89=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84(#1?= =?UTF-8?q?35)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/AdminEventUserController.java | 3 ++- .../repository/EventUserRepository.java | 8 +++++-- .../repository/EventUserSpecification.java | 23 +++++++++++++++++++ .../eventuser/service/EventUserService.java | 12 ++++++++-- 4 files changed, 41 insertions(+), 5 deletions(-) create mode 100644 src/main/java/hyundai/softeer/orange/eventuser/repository/EventUserSpecification.java diff --git a/src/main/java/hyundai/softeer/orange/admin/controller/AdminEventUserController.java b/src/main/java/hyundai/softeer/orange/admin/controller/AdminEventUserController.java index 4c461353..2d5475f6 100644 --- a/src/main/java/hyundai/softeer/orange/admin/controller/AdminEventUserController.java +++ b/src/main/java/hyundai/softeer/orange/admin/controller/AdminEventUserController.java @@ -33,10 +33,11 @@ public class AdminEventUserController { @GetMapping public ResponseEntity getEventUsers( @RequestParam(name="search", required = false, defaultValue = "") String search, + @RequestParam(name="field", required = false, defaultValue = "name") String field, @RequestParam(name="page", required = false, defaultValue = "0") @Min(0) Integer page, @RequestParam(name="size", required = false, defaultValue = "10") @Min(1) Integer size ) { - var userPageDto = eventUserService.getUserBySearch(search, page, size); + var userPageDto = eventUserService.getUserBySearch(search, field, page, size); return ResponseEntity.ok(userPageDto); } diff --git a/src/main/java/hyundai/softeer/orange/eventuser/repository/EventUserRepository.java b/src/main/java/hyundai/softeer/orange/eventuser/repository/EventUserRepository.java index a0eb55c3..de2f16e1 100644 --- a/src/main/java/hyundai/softeer/orange/eventuser/repository/EventUserRepository.java +++ b/src/main/java/hyundai/softeer/orange/eventuser/repository/EventUserRepository.java @@ -4,8 +4,12 @@ import hyundai.softeer.orange.eventuser.entity.EventUser; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.FluentQuery; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; @@ -13,7 +17,7 @@ import java.util.Optional; @Repository -public interface EventUserRepository extends JpaRepository, CustomEventUserRepository { +public interface EventUserRepository extends JpaRepository, CustomEventUserRepository, JpaSpecificationExecutor { Optional findByUserNameAndPhoneNumber(String userName, String phoneNumber); @@ -32,4 +36,4 @@ public interface EventUserRepository extends JpaRepository, Cus "JOIN event_metadata e ON e.event_frame_id = ef.id " + "WHERE e.id = :rawEventId", nativeQuery = true) List findAllUserScoreByDrawEventId(@Param("rawEventId") long rawEventId); -} + } diff --git a/src/main/java/hyundai/softeer/orange/eventuser/repository/EventUserSpecification.java b/src/main/java/hyundai/softeer/orange/eventuser/repository/EventUserSpecification.java new file mode 100644 index 00000000..22571a9d --- /dev/null +++ b/src/main/java/hyundai/softeer/orange/eventuser/repository/EventUserSpecification.java @@ -0,0 +1,23 @@ +package hyundai.softeer.orange.eventuser.repository; + +import hyundai.softeer.orange.event.common.entity.EventFrame; +import hyundai.softeer.orange.eventuser.entity.EventUser; +import jakarta.persistence.criteria.Fetch; +import jakarta.persistence.criteria.Join; +import jakarta.persistence.criteria.JoinType; +import org.springframework.data.jpa.domain.Specification; + +public class EventUserSpecification { + public static Specification search(String search, String field) { + return (user, query, cb) -> { + user.fetch("eventFrame"); + +// Join join = user.join("eventFrame", JoinType.LEFT); + + if("userName".equals(field)) return cb.like(user.get("userName"), "%" + search + "%"); + else if ("phoneNumber".equals(field)) return cb.like(user.get("phoneNumber"), "%" + search + "%"); + else if ("frameId".equals(field)) return cb.like(user.get("eventFrame").get("eventFrame").get("frameId"), "%" + search + "%"); + return cb.conjunction(); + }; + } +} diff --git a/src/main/java/hyundai/softeer/orange/eventuser/service/EventUserService.java b/src/main/java/hyundai/softeer/orange/eventuser/service/EventUserService.java index 387cfa07..a0e03c94 100644 --- a/src/main/java/hyundai/softeer/orange/eventuser/service/EventUserService.java +++ b/src/main/java/hyundai/softeer/orange/eventuser/service/EventUserService.java @@ -15,12 +15,14 @@ import hyundai.softeer.orange.eventuser.entity.EventUser; import hyundai.softeer.orange.eventuser.exception.EventUserException; import hyundai.softeer.orange.eventuser.repository.EventUserRepository; +import hyundai.softeer.orange.eventuser.repository.EventUserSpecification; import lombok.RequiredArgsConstructor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.domain.Specification; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -53,10 +55,16 @@ public TokenDto login(RequestUserDto dto) { } @Transactional(readOnly = true) - public EventUserPageDto getUserBySearch(String search, int page, int size) { + public EventUserPageDto getUserBySearch(String search, String field, int page, int size) { PageRequest pageRequest = PageRequest.of(page, size); - Page userPage = eventUserRepository.findBySearch(search, pageRequest); + Specification searchSpec = EventUserSpecification.search(search, field); + + Page userPage = eventUserRepository.findBy( + searchSpec, + (q) -> q.page(pageRequest) + ); + return EventUserPageDto.from(userPage); } From fdeb66eb42d62a694dd707a7e370dfad376e5a5f Mon Sep 17 00:00:00 2001 From: blaxsior Date: Thu, 22 Aug 2024 18:50:44 +0900 Subject: [PATCH 2/3] =?UTF-8?q?[feat]=20jpa=20specification=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EB=B0=9C=EC=83=9D=ED=95=98=EB=8D=98=20N+1=20/=20fe?= =?UTF-8?q?tch=20=EC=95=88=EB=90=98=EB=8A=94=20=EB=AC=B8=EC=A0=9C=EB=A5=BC?= =?UTF-8?q?=20queryDSL=EB=A1=9C=20=ED=95=B4=EA=B2=B0(#135)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 7 +++++ .../controller/AdminEventUserController.java | 2 +- .../softeer/orange/config/QueryDSLConfig.java | 19 ++++++++++++++ .../repository/CustomEventUserRepository.java | 4 +++ .../CustomEventUserRepositoryImpl.java | 26 +++++++++++++++++++ .../repository/EventUserSpecification.java | 23 ---------------- .../eventuser/service/EventUserService.java | 12 +-------- 7 files changed, 58 insertions(+), 35 deletions(-) create mode 100644 src/main/java/hyundai/softeer/orange/config/QueryDSLConfig.java delete mode 100644 src/main/java/hyundai/softeer/orange/eventuser/repository/EventUserSpecification.java diff --git a/build.gradle b/build.gradle index ec5da408..bdae6ed0 100644 --- a/build.gradle +++ b/build.gradle @@ -70,6 +70,13 @@ dependencies { // sms api implementation 'net.nurigo:sdk:4.3.0' + + // queryDsl + implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta' + annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta" + annotationProcessor "jakarta.annotation:jakarta.annotation-api" + annotationProcessor "jakarta.persistence:jakarta.persistence-api" + } tasks.named('test') { diff --git a/src/main/java/hyundai/softeer/orange/admin/controller/AdminEventUserController.java b/src/main/java/hyundai/softeer/orange/admin/controller/AdminEventUserController.java index 2d5475f6..e5b0b2fb 100644 --- a/src/main/java/hyundai/softeer/orange/admin/controller/AdminEventUserController.java +++ b/src/main/java/hyundai/softeer/orange/admin/controller/AdminEventUserController.java @@ -33,7 +33,7 @@ public class AdminEventUserController { @GetMapping public ResponseEntity getEventUsers( @RequestParam(name="search", required = false, defaultValue = "") String search, - @RequestParam(name="field", required = false, defaultValue = "name") String field, + @RequestParam(name="field", required = false, defaultValue = "userName") String field, @RequestParam(name="page", required = false, defaultValue = "0") @Min(0) Integer page, @RequestParam(name="size", required = false, defaultValue = "10") @Min(1) Integer size ) { diff --git a/src/main/java/hyundai/softeer/orange/config/QueryDSLConfig.java b/src/main/java/hyundai/softeer/orange/config/QueryDSLConfig.java new file mode 100644 index 00000000..b21d2170 --- /dev/null +++ b/src/main/java/hyundai/softeer/orange/config/QueryDSLConfig.java @@ -0,0 +1,19 @@ +package hyundai.softeer.orange.config; + + +import com.querydsl.jpa.impl.JPAQueryFactory; +import jakarta.persistence.EntityManager; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +@RequiredArgsConstructor +public class QueryDSLConfig { + private final EntityManager entityManager; + + @Bean + public JPAQueryFactory jpaQueryFactory(){ + return new JPAQueryFactory(entityManager); + } +} \ No newline at end of file diff --git a/src/main/java/hyundai/softeer/orange/eventuser/repository/CustomEventUserRepository.java b/src/main/java/hyundai/softeer/orange/eventuser/repository/CustomEventUserRepository.java index fbb4eae7..947a4028 100644 --- a/src/main/java/hyundai/softeer/orange/eventuser/repository/CustomEventUserRepository.java +++ b/src/main/java/hyundai/softeer/orange/eventuser/repository/CustomEventUserRepository.java @@ -1,10 +1,14 @@ package hyundai.softeer.orange.eventuser.repository; import hyundai.softeer.orange.eventuser.dto.EventUserScoreDto; +import hyundai.softeer.orange.eventuser.entity.EventUser; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import java.util.Collection; import java.util.List; public interface CustomEventUserRepository { void updateScoreMany(List dto); + Page findBySearch(String search, String field, Pageable pageable); } diff --git a/src/main/java/hyundai/softeer/orange/eventuser/repository/CustomEventUserRepositoryImpl.java b/src/main/java/hyundai/softeer/orange/eventuser/repository/CustomEventUserRepositoryImpl.java index 6172335c..760cfa0a 100644 --- a/src/main/java/hyundai/softeer/orange/eventuser/repository/CustomEventUserRepositoryImpl.java +++ b/src/main/java/hyundai/softeer/orange/eventuser/repository/CustomEventUserRepositoryImpl.java @@ -1,7 +1,14 @@ package hyundai.softeer.orange.eventuser.repository; +import com.querydsl.jpa.impl.JPAQueryFactory; +import hyundai.softeer.orange.event.common.entity.QEventFrame; import hyundai.softeer.orange.eventuser.dto.EventUserScoreDto; +import hyundai.softeer.orange.eventuser.entity.EventUser; +import hyundai.softeer.orange.eventuser.entity.QEventUser; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; import org.springframework.jdbc.core.BatchPreparedStatementSetter; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; @@ -14,6 +21,7 @@ @Repository public class CustomEventUserRepositoryImpl implements CustomEventUserRepository { private final JdbcTemplate jdbcTemplate; + private final JPAQueryFactory queryFactory; @Override public void updateScoreMany(List userScores) { @@ -33,4 +41,22 @@ public int getBatchSize() { } }); } + + @Override + public Page findBySearch(String search, String field, Pageable pageable) { + QEventUser user = QEventUser.eventUser; + QEventFrame eventFrame = QEventFrame.eventFrame; + + var query = queryFactory.select(user) + .from(user) + .leftJoin(user.eventFrame, eventFrame) + .fetchJoin(); + + if("userName".equals(field)) query.where(user.userName.contains(search)); + else if("phoneNumber".equals(field))query.where(user.phoneNumber.contains(search)); + else if("frameId".equals(field)) query.where(user.eventFrame.frameId.contains(search)); + + var data = query.offset(pageable.getOffset()).limit(pageable.getPageSize()).fetch(); + return new PageImpl<>(data, pageable, data.size()); + } } diff --git a/src/main/java/hyundai/softeer/orange/eventuser/repository/EventUserSpecification.java b/src/main/java/hyundai/softeer/orange/eventuser/repository/EventUserSpecification.java deleted file mode 100644 index 22571a9d..00000000 --- a/src/main/java/hyundai/softeer/orange/eventuser/repository/EventUserSpecification.java +++ /dev/null @@ -1,23 +0,0 @@ -package hyundai.softeer.orange.eventuser.repository; - -import hyundai.softeer.orange.event.common.entity.EventFrame; -import hyundai.softeer.orange.eventuser.entity.EventUser; -import jakarta.persistence.criteria.Fetch; -import jakarta.persistence.criteria.Join; -import jakarta.persistence.criteria.JoinType; -import org.springframework.data.jpa.domain.Specification; - -public class EventUserSpecification { - public static Specification search(String search, String field) { - return (user, query, cb) -> { - user.fetch("eventFrame"); - -// Join join = user.join("eventFrame", JoinType.LEFT); - - if("userName".equals(field)) return cb.like(user.get("userName"), "%" + search + "%"); - else if ("phoneNumber".equals(field)) return cb.like(user.get("phoneNumber"), "%" + search + "%"); - else if ("frameId".equals(field)) return cb.like(user.get("eventFrame").get("eventFrame").get("frameId"), "%" + search + "%"); - return cb.conjunction(); - }; - } -} diff --git a/src/main/java/hyundai/softeer/orange/eventuser/service/EventUserService.java b/src/main/java/hyundai/softeer/orange/eventuser/service/EventUserService.java index a0e03c94..f3b22fab 100644 --- a/src/main/java/hyundai/softeer/orange/eventuser/service/EventUserService.java +++ b/src/main/java/hyundai/softeer/orange/eventuser/service/EventUserService.java @@ -8,21 +8,17 @@ import hyundai.softeer.orange.core.jwt.JWTManager; import hyundai.softeer.orange.event.common.entity.EventFrame; import hyundai.softeer.orange.event.common.repository.EventFrameRepository; -import hyundai.softeer.orange.eventuser.dto.EventUserOnAdminDto; import hyundai.softeer.orange.eventuser.dto.EventUserPageDto; import hyundai.softeer.orange.eventuser.dto.RequestAuthCodeDto; import hyundai.softeer.orange.eventuser.dto.RequestUserDto; import hyundai.softeer.orange.eventuser.entity.EventUser; import hyundai.softeer.orange.eventuser.exception.EventUserException; import hyundai.softeer.orange.eventuser.repository.EventUserRepository; -import hyundai.softeer.orange.eventuser.repository.EventUserSpecification; import lombok.RequiredArgsConstructor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import org.springframework.data.jpa.domain.Specification; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -57,13 +53,7 @@ public TokenDto login(RequestUserDto dto) { @Transactional(readOnly = true) public EventUserPageDto getUserBySearch(String search, String field, int page, int size) { PageRequest pageRequest = PageRequest.of(page, size); - - Specification searchSpec = EventUserSpecification.search(search, field); - - Page userPage = eventUserRepository.findBy( - searchSpec, - (q) -> q.page(pageRequest) - ); + Page userPage = eventUserRepository.findBySearch(search, field, pageRequest); return EventUserPageDto.from(userPage); } From e7d19fba5b4dcd190b4557146853bbbfcf183a1e Mon Sep 17 00:00:00 2001 From: blaxsior Date: Thu, 22 Aug 2024 19:45:56 +0900 Subject: [PATCH 3/3] =?UTF-8?q?[fix]=20QueryDSL=EB=A1=9C=20=EC=9D=B8?= =?UTF-8?q?=ED=95=B4=20=EB=B0=9C=EC=83=9D=ED=95=98=EB=8A=94=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=ED=99=98=EA=B2=BD=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0=20(#135)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../softeer/orange/support/TCDataJpaTest.java | 2 ++ .../softeer/orange/support/TestConfig.java | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 src/test/java/hyundai/softeer/orange/support/TestConfig.java diff --git a/src/test/java/hyundai/softeer/orange/support/TCDataJpaTest.java b/src/test/java/hyundai/softeer/orange/support/TCDataJpaTest.java index 3ca7cb24..ba9e251f 100644 --- a/src/test/java/hyundai/softeer/orange/support/TCDataJpaTest.java +++ b/src/test/java/hyundai/softeer/orange/support/TCDataJpaTest.java @@ -2,6 +2,7 @@ import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; import org.springframework.test.context.ActiveProfiles; import org.testcontainers.junit.jupiter.Testcontainers; @@ -16,5 +17,6 @@ @Testcontainers @DataJpaTest @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) +@Import(TestConfig.class) public @interface TCDataJpaTest { } diff --git a/src/test/java/hyundai/softeer/orange/support/TestConfig.java b/src/test/java/hyundai/softeer/orange/support/TestConfig.java new file mode 100644 index 00000000..9b5631d4 --- /dev/null +++ b/src/test/java/hyundai/softeer/orange/support/TestConfig.java @@ -0,0 +1,18 @@ +package hyundai.softeer.orange.support; + +import com.querydsl.jpa.impl.JPAQueryFactory; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; + +@TestConfiguration +public class TestConfig { + @PersistenceContext + private EntityManager entityManager; + + @Bean + public JPAQueryFactory jpaQueryFactory() { + return new JPAQueryFactory(entityManager); + } +}