diff --git a/backend/.gitignore b/backend/.gitignore index b506e3002..728a2c212 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -37,3 +37,6 @@ out/ ### VS Code ### .vscode/ + +### QUERYDSL ### +/src/main/generated/ diff --git a/backend/build.gradle b/backend/build.gradle index b8d91cef4..a3d6059a5 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -47,6 +47,12 @@ dependencies { testImplementation 'io.cucumber:cucumber-junit-platform-engine:7.13.0' testImplementation 'org.junit.platform:junit-platform-suite:1.8.2' + // Querydsl + implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta' + annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta" + annotationProcessor "jakarta.annotation:jakarta.annotation-api" + annotationProcessor "jakarta.persistence:jakarta.persistence-api" + // Flyway implementation 'org.flywaydb:flyway-core' implementation 'org.flywaydb:flyway-mysql' @@ -64,3 +70,21 @@ dependencies { tasks.named('test') { useJUnitPlatform() } + +// 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] +} + +// gradle clean 시에 QClass 디렉토리 삭제 +clean { + delete file(generated) +} diff --git a/backend/src/main/java/com/festago/config/QuerydslConfig.java b/backend/src/main/java/com/festago/config/QuerydslConfig.java new file mode 100644 index 000000000..5f2fd66c5 --- /dev/null +++ b/backend/src/main/java/com/festago/config/QuerydslConfig.java @@ -0,0 +1,19 @@ +package com.festago.config; + +import com.querydsl.jpa.impl.JPAQueryFactory; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class QuerydslConfig { + + @PersistenceContext + private EntityManager entityManager; + + @Bean + public JPAQueryFactory jpaQueryFactory() { + return new JPAQueryFactory(entityManager); + } +} diff --git a/backend/src/main/java/com/festago/festival/application/FestivalService.java b/backend/src/main/java/com/festago/festival/application/FestivalService.java index 5b8185718..a7a298e0d 100644 --- a/backend/src/main/java/com/festago/festival/application/FestivalService.java +++ b/backend/src/main/java/com/festago/festival/application/FestivalService.java @@ -51,7 +51,7 @@ private void validate(Festival festival) { @Transactional(readOnly = true) public FestivalsResponse findFestivals(FestivalFilter festivalFilter) { - List festivals = festivalRepository.findAll(festivalFilter.getSpecification(LocalDate.now(clock))); + List festivals = festivalRepository.findByFilter(festivalFilter, LocalDate.now(clock)); return FestivalsResponse.from(festivals); } diff --git a/backend/src/main/java/com/festago/festival/repository/FestivalFilter.java b/backend/src/main/java/com/festago/festival/repository/FestivalFilter.java index 81f60d250..7a328b631 100644 --- a/backend/src/main/java/com/festago/festival/repository/FestivalFilter.java +++ b/backend/src/main/java/com/festago/festival/repository/FestivalFilter.java @@ -2,21 +2,11 @@ import com.festago.common.exception.BadRequestException; import com.festago.common.exception.ErrorCode; -import com.festago.festival.domain.Festival; -import java.time.LocalDate; -import java.util.function.Function; -import org.springframework.data.jpa.domain.Specification; public enum FestivalFilter { - PROGRESS(FestivalSpecification::progress), - PLANNED(FestivalSpecification::planned), - END(FestivalSpecification::end); - - private final Function> filter; - - FestivalFilter(Function> filter) { - this.filter = filter; - } + PROGRESS, + PLANNED, + END; public static FestivalFilter from(String filterName) { return switch (filterName.toUpperCase()) { @@ -26,8 +16,4 @@ public static FestivalFilter from(String filterName) { default -> throw new BadRequestException(ErrorCode.INVALID_FESTIVAL_FILTER); }; } - - public Specification getSpecification(LocalDate currentTime) { - return filter.apply(currentTime); - } } diff --git a/backend/src/main/java/com/festago/festival/repository/FestivalRepository.java b/backend/src/main/java/com/festago/festival/repository/FestivalRepository.java index 032b89a68..a71c6a272 100644 --- a/backend/src/main/java/com/festago/festival/repository/FestivalRepository.java +++ b/backend/src/main/java/com/festago/festival/repository/FestivalRepository.java @@ -2,8 +2,7 @@ import com.festago.festival.domain.Festival; import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.JpaSpecificationExecutor; -public interface FestivalRepository extends JpaRepository, JpaSpecificationExecutor { +public interface FestivalRepository extends JpaRepository, FestivalRepositoryCustom { } diff --git a/backend/src/main/java/com/festago/festival/repository/FestivalRepositoryCustom.java b/backend/src/main/java/com/festago/festival/repository/FestivalRepositoryCustom.java new file mode 100644 index 000000000..531f1f64c --- /dev/null +++ b/backend/src/main/java/com/festago/festival/repository/FestivalRepositoryCustom.java @@ -0,0 +1,10 @@ +package com.festago.festival.repository; + +import com.festago.festival.domain.Festival; +import java.time.LocalDate; +import java.util.List; + +public interface FestivalRepositoryCustom { + + List findByFilter(FestivalFilter festivalFilter, LocalDate currentTime); +} diff --git a/backend/src/main/java/com/festago/festival/repository/FestivalRepositoryCustomImpl.java b/backend/src/main/java/com/festago/festival/repository/FestivalRepositoryCustomImpl.java new file mode 100644 index 000000000..db79bfcb6 --- /dev/null +++ b/backend/src/main/java/com/festago/festival/repository/FestivalRepositoryCustomImpl.java @@ -0,0 +1,42 @@ +package com.festago.festival.repository; + +import static com.festago.festival.domain.QFestival.festival; + +import com.festago.festival.domain.Festival; +import com.querydsl.jpa.impl.JPAQueryFactory; +import java.time.LocalDate; +import java.util.List; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public class FestivalRepositoryCustomImpl implements FestivalRepositoryCustom { + + private final JPAQueryFactory queryFactory; + + @Override + public List findByFilter(FestivalFilter festivalFilter, LocalDate currentTime) { + return switch (festivalFilter) { + case PLANNED -> plannedFestivals(currentTime); + case PROGRESS -> progressFestivals(currentTime); + case END -> endFestivals(currentTime); + }; + } + + private List plannedFestivals(LocalDate currentTime) { + return queryFactory.selectFrom(festival) + .where(festival.startDate.gt(currentTime)) + .orderBy(festival.startDate.asc()).fetch(); + } + + private List progressFestivals(LocalDate currentTime) { + return queryFactory.selectFrom(festival) + .where(festival.startDate.loe(currentTime).and(festival.endDate.goe(currentTime))) + .orderBy(festival.startDate.asc()).fetch(); + } + + private List endFestivals(LocalDate currentTime) { + return queryFactory.selectFrom(festival) + .where(festival.endDate.lt(currentTime)) + .orderBy(festival.endDate.desc()).fetch(); + } +} diff --git a/backend/src/main/java/com/festago/festival/repository/FestivalSpecification.java b/backend/src/main/java/com/festago/festival/repository/FestivalSpecification.java deleted file mode 100644 index 50e7efb5c..000000000 --- a/backend/src/main/java/com/festago/festival/repository/FestivalSpecification.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.festago.festival.repository; - -import com.festago.festival.domain.Festival; -import java.time.LocalDate; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; -import org.springframework.data.jpa.domain.Specification; - -@NoArgsConstructor(access = AccessLevel.PRIVATE) -public class FestivalSpecification { - - private static final String START_DATE = "startDate"; - private static final String END_DATE = "endDate"; - - public static Specification progress(LocalDate currentTime) { - return (root, query, criteriaBuilder) -> { - query.orderBy(criteriaBuilder.asc(root.get(START_DATE))); - return criteriaBuilder.and( - criteriaBuilder.lessThanOrEqualTo(root.get(START_DATE), currentTime), - criteriaBuilder.greaterThanOrEqualTo(root.get(END_DATE), currentTime) - ); - }; - } - - public static Specification planned(LocalDate currentTime) { - return (root, query, criteriaBuilder) -> { - query.orderBy(criteriaBuilder.asc(root.get(START_DATE))); - return criteriaBuilder.greaterThan(root.get(START_DATE), currentTime); - }; - } - - public static Specification end(LocalDate currentTime) { - return (root, query, criteriaBuilder) -> { - query.orderBy(criteriaBuilder.desc(root.get(END_DATE))); - return criteriaBuilder.lessThan(root.get(END_DATE), currentTime); - }; - } -} diff --git a/backend/src/test/java/com/festago/fcm/repository/MemberFCMRepositoryTest.java b/backend/src/test/java/com/festago/fcm/repository/MemberFCMRepositoryTest.java index 3ea135724..f4b3fa624 100644 --- a/backend/src/test/java/com/festago/fcm/repository/MemberFCMRepositoryTest.java +++ b/backend/src/test/java/com/festago/fcm/repository/MemberFCMRepositoryTest.java @@ -6,12 +6,12 @@ import com.festago.fcm.domain.MemberFCM; import com.festago.member.domain.Member; import com.festago.member.repository.MemberRepository; +import com.festago.support.RepositoryTest; import java.util.List; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; -@DataJpaTest +@RepositoryTest class MemberFCMRepositoryTest { @Autowired diff --git a/backend/src/test/java/com/festago/festival/repository/FestivalRepositoryTest.java b/backend/src/test/java/com/festago/festival/repository/FestivalRepositoryTest.java index 81e13a050..f36b269be 100644 --- a/backend/src/test/java/com/festago/festival/repository/FestivalRepositoryTest.java +++ b/backend/src/test/java/com/festago/festival/repository/FestivalRepositoryTest.java @@ -6,6 +6,7 @@ import com.festago.festival.domain.Festival; import com.festago.school.domain.School; import com.festago.school.repository.SchoolRepository; +import com.festago.support.RepositoryTest; import java.time.LocalDate; import java.util.List; import org.junit.jupiter.api.BeforeEach; @@ -14,11 +15,10 @@ import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; @DisplayNameGeneration(ReplaceUnderscores.class) @SuppressWarnings("NonAsciiCharacters") -@DataJpaTest +@RepositoryTest class FestivalRepositoryTest { private static final String CURRENT_FESTIVAL = "현재 축제"; @@ -56,7 +56,7 @@ class 진행_에정_축제_반환 { prepareNotOrderedFestivals(); // when - List actual = festivalRepository.findAll(filter.getSpecification(now)); + List actual = festivalRepository.findByFilter(filter, now); // then assertSoftly(softAssertions -> { @@ -77,7 +77,7 @@ class 진행_에정_축제_반환 { new Festival("festival1", now.plusDays(1), now.plusDays(10), school)); // when - List actual = festivalRepository.findAll(filter.getSpecification(now)); + List actual = festivalRepository.findByFilter(filter, now); // then assertThat(actual).isEqualTo(List.of(festival1, festival2, festival3)); @@ -94,7 +94,7 @@ class 진행_축제_반환 { prepareNotOrderedFestivals(); // when - List actual = festivalRepository.findAll(filter.getSpecification(now)); + List actual = festivalRepository.findByFilter(filter, now); // then assertSoftly(softAssertions -> { @@ -115,7 +115,7 @@ class 진행_축제_반환 { new Festival("festival1", now.minusDays(3), now.plusDays(10), school)); // when - List actual = festivalRepository.findAll(filter.getSpecification(now)); + List actual = festivalRepository.findByFilter(filter, now); // then assertThat(actual).isEqualTo(List.of(festival1, festival2, festival3)); @@ -132,7 +132,7 @@ class 종료_축제_반환 { prepareNotOrderedFestivals(); // when - List actual = festivalRepository.findAll(filter.getSpecification(now)); + List actual = festivalRepository.findByFilter(filter, now); // then assertSoftly(softAssertions -> { @@ -153,7 +153,7 @@ class 종료_축제_반환 { new Festival("festival1", now.minusDays(10), now.minusDays(3), school)); // when - List actual = festivalRepository.findAll(filter.getSpecification(now)); + List actual = festivalRepository.findByFilter(filter, now); // then assertThat(actual).isEqualTo(List.of(festival3, festival2, festival1)); diff --git a/backend/src/test/java/com/festago/member/repository/MemberRepositoryTest.java b/backend/src/test/java/com/festago/member/repository/MemberRepositoryTest.java index c66c3678c..84401f004 100644 --- a/backend/src/test/java/com/festago/member/repository/MemberRepositoryTest.java +++ b/backend/src/test/java/com/festago/member/repository/MemberRepositoryTest.java @@ -5,16 +5,16 @@ import com.festago.member.domain.Member; import com.festago.support.MemberFixture; +import com.festago.support.RepositoryTest; import jakarta.persistence.EntityManager; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; @DisplayNameGeneration(ReplaceUnderscores.class) @SuppressWarnings("NonAsciiCharacters") -@DataJpaTest +@RepositoryTest class MemberRepositoryTest { @Autowired diff --git a/backend/src/test/java/com/festago/stage/repository/StageRepositoryTest.java b/backend/src/test/java/com/festago/stage/repository/StageRepositoryTest.java index 7f90511d1..a47dcaa71 100644 --- a/backend/src/test/java/com/festago/stage/repository/StageRepositoryTest.java +++ b/backend/src/test/java/com/festago/stage/repository/StageRepositoryTest.java @@ -9,6 +9,7 @@ import com.festago.school.repository.SchoolRepository; import com.festago.stage.domain.Stage; import com.festago.support.FestivalFixture; +import com.festago.support.RepositoryTest; import com.festago.support.SchoolFixture; import com.festago.support.StageFixture; import com.festago.support.TicketFixture; @@ -21,11 +22,10 @@ import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; @DisplayNameGeneration(ReplaceUnderscores.class) @SuppressWarnings("NonAsciiCharacters") -@DataJpaTest +@RepositoryTest class StageRepositoryTest { @Autowired diff --git a/backend/src/test/java/com/festago/student/repository/StudentRepositoryTest.java b/backend/src/test/java/com/festago/student/repository/StudentRepositoryTest.java index 6076dab94..976ece1ae 100644 --- a/backend/src/test/java/com/festago/student/repository/StudentRepositoryTest.java +++ b/backend/src/test/java/com/festago/student/repository/StudentRepositoryTest.java @@ -8,6 +8,7 @@ import com.festago.school.repository.SchoolRepository; import com.festago.student.domain.Student; import com.festago.support.MemberFixture; +import com.festago.support.RepositoryTest; import com.festago.support.SchoolFixture; import com.festago.support.StudentFixture; import java.util.Optional; @@ -15,11 +16,10 @@ import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; @DisplayNameGeneration(ReplaceUnderscores.class) @SuppressWarnings("NonAsciiCharacters") -@DataJpaTest +@RepositoryTest class StudentRepositoryTest { @Autowired diff --git a/backend/src/test/java/com/festago/support/RepositoryTest.java b/backend/src/test/java/com/festago/support/RepositoryTest.java new file mode 100644 index 000000000..e4b2414d5 --- /dev/null +++ b/backend/src/test/java/com/festago/support/RepositoryTest.java @@ -0,0 +1,14 @@ +package com.festago.support; + +import com.festago.config.QuerydslConfig; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; + +@Retention(RetentionPolicy.RUNTIME) +@DataJpaTest +@Import(QuerydslConfig.class) +public @interface RepositoryTest { + +} diff --git a/backend/src/test/java/com/festago/ticket/repository/TicketRepositoryTest.java b/backend/src/test/java/com/festago/ticket/repository/TicketRepositoryTest.java index c093a5b69..6145fbebf 100644 --- a/backend/src/test/java/com/festago/ticket/repository/TicketRepositoryTest.java +++ b/backend/src/test/java/com/festago/ticket/repository/TicketRepositoryTest.java @@ -9,6 +9,7 @@ import com.festago.stage.domain.Stage; import com.festago.stage.repository.StageRepository; import com.festago.support.FestivalFixture; +import com.festago.support.RepositoryTest; import com.festago.support.SchoolFixture; import com.festago.support.StageFixture; import com.festago.support.TicketFixture; @@ -19,11 +20,10 @@ import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; @DisplayNameGeneration(ReplaceUnderscores.class) @SuppressWarnings("NonAsciiCharacters") -@DataJpaTest +@RepositoryTest class TicketRepositoryTest { @Autowired diff --git a/backend/src/test/java/com/festago/ticketing/repository/MemberTicketRepositoryTest.java b/backend/src/test/java/com/festago/ticketing/repository/MemberTicketRepositoryTest.java index 5e78bc0c7..c4d7beb56 100644 --- a/backend/src/test/java/com/festago/ticketing/repository/MemberTicketRepositoryTest.java +++ b/backend/src/test/java/com/festago/ticketing/repository/MemberTicketRepositoryTest.java @@ -15,6 +15,7 @@ import com.festago.support.FestivalFixture; import com.festago.support.MemberFixture; import com.festago.support.MemberTicketFixture; +import com.festago.support.RepositoryTest; import com.festago.support.SchoolFixture; import com.festago.support.StageFixture; import com.festago.ticket.repository.TicketRepository; @@ -27,16 +28,15 @@ import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.context.annotation.Import; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; @Import(JpaAuditingConfig.class) -@DataJpaTest @DisplayNameGeneration(ReplaceUnderscores.class) @SuppressWarnings("NonAsciiCharacters") +@RepositoryTest class MemberTicketRepositoryTest { @Autowired