Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BE] feat: Specification을 Querydsl로 대체한다 (#604) #609

Merged
merged 11 commits into from
Nov 19, 2023
3 changes: 3 additions & 0 deletions backend/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,6 @@ out/

### VS Code ###
.vscode/

### QUERYDSL ###
/src/main/generated/
24 changes: 24 additions & 0 deletions backend/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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)
}
19 changes: 19 additions & 0 deletions backend/src/main/java/com/festago/config/QuerydslConfig.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ private void validate(Festival festival) {

@Transactional(readOnly = true)
public FestivalsResponse findFestivals(FestivalFilter festivalFilter) {
List<Festival> festivals = festivalRepository.findAll(festivalFilter.getSpecification(LocalDate.now(clock)));
List<Festival> festivals = festivalRepository.findByFilter(festivalFilter, LocalDate.now(clock));
return FestivalsResponse.from(festivals);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<LocalDate, Specification<Festival>> filter;

FestivalFilter(Function<LocalDate, Specification<Festival>> filter) {
this.filter = filter;
}
PROGRESS,
PLANNED,
END;

public static FestivalFilter from(String filterName) {
return switch (filterName.toUpperCase()) {
Expand All @@ -26,8 +16,4 @@ public static FestivalFilter from(String filterName) {
default -> throw new BadRequestException(ErrorCode.INVALID_FESTIVAL_FILTER);
};
}

public Specification<Festival> getSpecification(LocalDate currentTime) {
return filter.apply(currentTime);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<Festival, Long>, JpaSpecificationExecutor<Festival> {
public interface FestivalRepository extends JpaRepository<Festival, Long>, FestivalRepositoryCustom {

}
Original file line number Diff line number Diff line change
@@ -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 {
Copy link
Collaborator

@seokjin8678 seokjin8678 Nov 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.single-repository-behavior
스프링 문서에는 접두사에 Custom을 사용하는데, 접미사로 Custom을 사용해주신 이유가 따로 있나요?


List<Festival> findByFilter(FestivalFilter festivalFilter, LocalDate currentTime);
}
Original file line number Diff line number Diff line change
@@ -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<Festival> findByFilter(FestivalFilter festivalFilter, LocalDate currentTime) {
return switch (festivalFilter) {
case PLANNED -> plannedFestivals(currentTime);
case PROGRESS -> progressFestivals(currentTime);
case END -> endFestivals(currentTime);
};
}

private List<Festival> plannedFestivals(LocalDate currentTime) {
return queryFactory.selectFrom(festival)
.where(festival.startDate.gt(currentTime))
.orderBy(festival.startDate.asc()).fetch();
}

private List<Festival> progressFestivals(LocalDate currentTime) {
return queryFactory.selectFrom(festival)
.where(festival.startDate.loe(currentTime).and(festival.endDate.goe(currentTime)))
.orderBy(festival.startDate.asc()).fetch();
}

private List<Festival> endFestivals(LocalDate currentTime) {
return queryFactory.selectFrom(festival)
.where(festival.endDate.lt(currentTime))
.orderBy(festival.endDate.desc()).fetch();
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 = "현재 축제";
Expand Down Expand Up @@ -56,7 +56,7 @@ class 진행_에정_축제_반환 {
prepareNotOrderedFestivals();

// when
List<Festival> actual = festivalRepository.findAll(filter.getSpecification(now));
List<Festival> actual = festivalRepository.findByFilter(filter, now);

// then
assertSoftly(softAssertions -> {
Expand All @@ -77,7 +77,7 @@ class 진행_에정_축제_반환 {
new Festival("festival1", now.plusDays(1), now.plusDays(10), school));

// when
List<Festival> actual = festivalRepository.findAll(filter.getSpecification(now));
List<Festival> actual = festivalRepository.findByFilter(filter, now);

// then
assertThat(actual).isEqualTo(List.of(festival1, festival2, festival3));
Expand All @@ -94,7 +94,7 @@ class 진행_축제_반환 {
prepareNotOrderedFestivals();

// when
List<Festival> actual = festivalRepository.findAll(filter.getSpecification(now));
List<Festival> actual = festivalRepository.findByFilter(filter, now);

// then
assertSoftly(softAssertions -> {
Expand All @@ -115,7 +115,7 @@ class 진행_축제_반환 {
new Festival("festival1", now.minusDays(3), now.plusDays(10), school));

// when
List<Festival> actual = festivalRepository.findAll(filter.getSpecification(now));
List<Festival> actual = festivalRepository.findByFilter(filter, now);

// then
assertThat(actual).isEqualTo(List.of(festival1, festival2, festival3));
Expand All @@ -132,7 +132,7 @@ class 종료_축제_반환 {
prepareNotOrderedFestivals();

// when
List<Festival> actual = festivalRepository.findAll(filter.getSpecification(now));
List<Festival> actual = festivalRepository.findByFilter(filter, now);

// then
assertSoftly(softAssertions -> {
Expand All @@ -153,7 +153,7 @@ class 종료_축제_반환 {
new Festival("festival1", now.minusDays(10), now.minusDays(3), school));

// when
List<Festival> actual = festivalRepository.findAll(filter.getSpecification(now));
List<Festival> actual = festivalRepository.findByFilter(filter, now);

// then
assertThat(actual).isEqualTo(List.of(festival3, festival2, festival1));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,18 @@
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;
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 StudentRepositoryTest {

@Autowired
Expand Down
14 changes: 14 additions & 0 deletions backend/src/test/java/com/festago/support/RepositoryTest.java
Original file line number Diff line number Diff line change
@@ -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 {

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand Down
Loading
Loading