Skip to content

Commit af31df1

Browse files
authored
feature : 동적 쿼리 적용, /api 경로 추가 (#63)
* feat : QueryDsl 프로젝트 적용(셋팅) * feat, refactor : ProblemSearchCondition, JPAQueryFactory 생성, gitkeep 제거 * refactor : 주석 제거 * refactor : QueryDSL 동적 쿼리 적용 * refactor : 사용하지 않는 메소드 제거 * feat : QueryDsl 프로젝트 적용(셋팅) * feat, refactor : ProblemSearchCondition, JPAQueryFactory 생성, gitkeep 제거 * refactor : 주석 제거 * refactor : QueryDSL 동적 쿼리 적용 * refactor : 사용하지 않는 메소드 제거 * refactor : title 검색 제거 ( 동적 쿼리 ) * refactor : URL 경로 /api 추가 (문제, 테스트케이스) * refactor : NPE 방지 ( @nonnull ) * refactor : ProblemSearchCondition 위치 변경 ( domain 계층 - model )
1 parent ffed84b commit af31df1

File tree

14 files changed

+130
-45
lines changed

14 files changed

+130
-45
lines changed

build.gradle

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,12 +90,20 @@ dependencies {
9090
implementation 'com.fasterxml.jackson.core:jackson-databind'
9191
implementation 'org.springframework.boot:spring-boot-configuration-processor'
9292

93+
94+
// QueryDSL
95+
implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
96+
annotationProcessor 'com.querydsl:querydsl-apt:5.0.0:jakarta'
97+
annotationProcessor 'jakarta.persistence:jakarta.persistence-api:3.1.0'
98+
annotationProcessor 'jakarta.annotation:jakarta.annotation-api:2.1.1'
99+
93100
// mongodb
94101
implementation 'org.springframework.boot:spring-boot-starter-data-mongodb'
95102

96103
// map-struct
97104
implementation 'org.mapstruct:mapstruct:1.6.3'
98105
annotationProcessor 'org.mapstruct:mapstruct-processor:1.6.3'
106+
99107
}
100108

101109
tasks.named('test') {

src/main/java/org/ezcode/codetest/application/problem/service/ProblemService.java

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
package org.ezcode.codetest.application.problem.service;
22

33
import org.ezcode.codetest.application.problem.dto.request.ProblemCreateRequest;
4+
import org.ezcode.codetest.domain.problem.model.ProblemSearchCondition;
45
import org.ezcode.codetest.application.problem.dto.request.ProblemUpdateRequest;
56
import org.ezcode.codetest.application.problem.dto.response.ProblemDetailResponse;
67
import org.ezcode.codetest.application.problem.dto.response.ProblemResponse;
78
import org.ezcode.codetest.domain.problem.model.entity.Problem;
8-
import org.ezcode.codetest.domain.problem.model.enums.Category;
99
import org.ezcode.codetest.domain.problem.service.ProblemDomainService;
1010
import org.ezcode.codetest.domain.user.model.entity.AuthUser;
1111
import org.ezcode.codetest.domain.user.model.entity.User;
@@ -39,14 +39,8 @@ public ProblemDetailResponse createProblem(ProblemCreateRequest requestDto, Auth
3939

4040
// 문제 전체 조회
4141
@Transactional(readOnly = true)
42-
public Page<ProblemResponse> getProblemsList(Pageable pageable, Category category) {
43-
Page<Problem> problems;
44-
45-
if (category != null) {
46-
problems = problemDomainService.getProblemsByCategoryList(category, pageable);
47-
} else {
48-
problems = problemDomainService.getProblemsList(pageable);
49-
}
42+
public Page<ProblemResponse> getProblemsList(Pageable pageable, ProblemSearchCondition searchCondition) {
43+
Page<Problem> problems = problemDomainService.getProblemBySearchCondition(pageable, searchCondition);
5044

5145
return problems.map(ProblemResponse::from); // Entity → DTO 변환
5246
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package org.ezcode.codetest.domain.problem.model;
2+
3+
import org.ezcode.codetest.domain.problem.model.enums.Category;
4+
5+
public record ProblemSearchCondition(
6+
Category category,
7+
8+
String difficulty
9+
10+
) {
11+
}

src/main/java/org/ezcode/codetest/domain/problem/repository/ProblemRepository.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,20 @@
22

33
import java.util.Optional;
44

5+
import org.ezcode.codetest.domain.problem.model.ProblemSearchCondition;
56
import org.ezcode.codetest.domain.problem.model.entity.Problem;
6-
import org.ezcode.codetest.domain.problem.model.enums.Category;
77
import org.springframework.data.domain.Page;
88
import org.springframework.data.domain.Pageable;
9+
import org.springframework.lang.NonNull;
910

1011
public interface ProblemRepository {
1112

1213
Problem save(Problem problem);
1314

14-
Page<Problem> findByCategoryAndIsDeletedIsFalse(Category category, Pageable pageable);
15-
16-
Page<Problem> findByIsDeletedIsFalse(Pageable pageable);
17-
1815
Optional<Problem> findByIdNotDeleted(Long problemId);
1916

17+
Page<Problem> searchByCondition(@NonNull Pageable pageable, @NonNull ProblemSearchCondition searchCondition);
18+
2019
void delete(Problem problem);
2120

2221
Optional<Problem> findProblemWithTestcasesById(Long problemId);

src/main/java/org/ezcode/codetest/domain/problem/service/ProblemDomainService.java

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
package org.ezcode.codetest.domain.problem.service;
22

3+
import org.ezcode.codetest.domain.problem.model.ProblemSearchCondition;
34
import org.ezcode.codetest.domain.problem.exception.ProblemException;
45
import org.ezcode.codetest.domain.problem.exception.code.ProblemExceptionCode;
56
import org.ezcode.codetest.domain.problem.model.ProblemInfo;
67
import org.ezcode.codetest.domain.problem.model.entity.Problem;
78
import org.ezcode.codetest.domain.problem.model.entity.ProblemSearchDocument;
8-
import org.ezcode.codetest.domain.problem.model.enums.Category;
99
import org.ezcode.codetest.domain.problem.repository.ProblemDocumentRepository;
1010
import org.ezcode.codetest.domain.problem.repository.ProblemRepository;
1111
import org.springframework.data.domain.Page;
@@ -36,15 +36,11 @@ public Problem createProblem(Problem problem) {
3636
return savedProblem;
3737
}
3838

39-
public Page<Problem> getProblemsByCategoryList(Category category, Pageable pageable) {
39+
public Page<Problem> getProblemBySearchCondition(Pageable pageable, ProblemSearchCondition searchCondition) {
4040

41-
return problemRepository.findByCategoryAndIsDeletedIsFalse(category, pageable);
41+
return problemRepository.searchByCondition(pageable, searchCondition);
4242
}
4343

44-
public Page<Problem> getProblemsList(Pageable pageable) {
45-
46-
return problemRepository.findByIsDeletedIsFalse(pageable);
47-
}
4844

4945
public Problem getProblem(Long problemId) {
5046

@@ -68,4 +64,5 @@ public ProblemInfo getProblemInfo(Long problemId) {
6864

6965
return new ProblemInfo(problem, problem.getTestcases());
7066
}
67+
7168
}

src/main/java/org/ezcode/codetest/infrastructure/persistence/config/.gitkeep

Whitespace-only changes.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package org.ezcode.codetest.infrastructure.persistence.config;
2+
3+
import org.springframework.context.annotation.Bean;
4+
import org.springframework.context.annotation.Configuration;
5+
6+
import com.querydsl.jpa.impl.JPAQueryFactory;
7+
8+
import jakarta.persistence.EntityManager;
9+
10+
@Configuration
11+
public class QuerydslConfig {
12+
13+
@Bean
14+
public JPAQueryFactory jpaQueryFactory(EntityManager entityManager) {
15+
return new JPAQueryFactory(entityManager);
16+
}
17+
}

src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/problem/ProblemJpaRepository.java

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,12 @@
33
import java.util.Optional;
44

55
import org.ezcode.codetest.domain.problem.model.entity.Problem;
6-
import org.ezcode.codetest.domain.problem.model.enums.Category;
7-
import org.springframework.data.domain.Page;
8-
import org.springframework.data.domain.Pageable;
96
import org.springframework.data.jpa.repository.JpaRepository;
107
import org.springframework.data.jpa.repository.Query;
118
import org.springframework.data.repository.query.Param;
129

1310
public interface ProblemJpaRepository extends JpaRepository<Problem, Long> {
1411

15-
Page<Problem> findByCategoryAndIsDeletedIsFalse(Category category, Pageable pageable);
16-
17-
Page<Problem> findByIsDeletedIsFalse(Pageable pageable);
18-
1912
@Query("SELECT p FROM Problem p WHERE p.isDeleted = false AND p.id = :problemId")
2013
Optional<Problem> findByIdNotDeleted(Long problemId);
2114

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package org.ezcode.codetest.infrastructure.persistence.repository.problem;
2+
3+
import java.util.List;
4+
5+
import org.ezcode.codetest.domain.problem.model.ProblemSearchCondition;
6+
import org.ezcode.codetest.domain.problem.model.entity.Problem;
7+
import org.ezcode.codetest.domain.problem.model.entity.QProblem;
8+
import org.springframework.data.domain.Page;
9+
import org.springframework.data.domain.PageImpl;
10+
import org.springframework.data.domain.Pageable;
11+
import org.springframework.stereotype.Repository;
12+
13+
import com.querydsl.core.BooleanBuilder;
14+
import com.querydsl.jpa.impl.JPAQueryFactory;
15+
16+
import lombok.RequiredArgsConstructor;
17+
18+
@Repository
19+
@RequiredArgsConstructor
20+
public class ProblemQueryRepositoryImpl implements ProblemRepositoryCustom {
21+
22+
private final JPAQueryFactory jpaQueryFactory;
23+
24+
@Override
25+
public Page<Problem> searchByCondition(Pageable pageable, ProblemSearchCondition searchCondition) {
26+
27+
QProblem problem = QProblem.problem;
28+
29+
// where 조건을 깔끔 하게 조립
30+
BooleanBuilder builder = new BooleanBuilder();
31+
32+
builder.and(problem.isDeleted.isFalse());
33+
34+
if(searchCondition.category() != null) {
35+
builder.and(problem.category.eq(searchCondition.category()));
36+
}
37+
38+
if(searchCondition.difficulty() != null) {
39+
builder.and(problem.difficulty.eq(searchCondition.difficulty()));
40+
}
41+
42+
List<Problem> content = jpaQueryFactory
43+
.selectFrom(problem)
44+
.where(builder)
45+
.offset(pageable.getOffset())
46+
.limit(pageable.getPageSize())
47+
.orderBy(problem.createdAt.desc())
48+
.fetch();
49+
50+
Long total = jpaQueryFactory
51+
.select(problem.count())
52+
.from(problem)
53+
.where(builder)
54+
.fetchOne();
55+
56+
return new PageImpl<>(content, pageable, total != null ? total : 0);
57+
}
58+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package org.ezcode.codetest.infrastructure.persistence.repository.problem;
2+
3+
import org.ezcode.codetest.domain.problem.model.ProblemSearchCondition;
4+
import org.ezcode.codetest.domain.problem.model.entity.Problem;
5+
import org.springframework.data.domain.Page;
6+
import org.springframework.data.domain.Pageable;
7+
8+
public interface ProblemRepositoryCustom {
9+
Page<Problem> searchByCondition(Pageable pageable, ProblemSearchCondition searchCondition);
10+
}

0 commit comments

Comments
 (0)