화면으로 보낼 데이터를 작업하려고 한다.
서비스 고민할 때 가장 첫 번째로 떠올렸던 '지역별 대표 품종' 쿼리부터 작업해보자.
의존성 설정
val queryDslVersion = "5.0.0"
dependencies {
implementation("com.querydsl:querydsl-jpa:${queryDslVersion}:jakarta")
annotationProcessor("com.querydsl:querydsl-apt:${queryDslVersion}:jakarta")
annotationProcessor("jakarta.annotation:jakarta.annotation-api")
annotationProcessor("jakarta.persistence:jakarta.persistence-api")
}
val querydslDir = "src/main/generated"
sourceSets {
main {
java {
srcDirs("src/main/java", querydslDir)
}
}
}
tasks.withType<JavaCompile> {
options.generatedSourceOutputDirectory.set(file(querydslDir))
}
tasks.named("clean") {
doLast {
file(querydslDir).deleteRecursively()
}
}
QueryDSL 을 사용할 것이기 때문에 gradle 설정을 추가한다.
항목 | 설명 |
querydsl-jpa:jakarta | JPA 기반 QueryDSL (Jakarta API용) |
querydsl-apt:jakarta | APT(Annotation Processing Tool) → Q클래스 자동 생성 |
jakarta.persistence-api | JPA 어노테이션 (@Entity 등) Jakarta 버전 |
jakarta.annotation-api | @Generated 등 Jakarta 관련 어노테이션 처리용 |
generatedSourceOutputDirectory | Q클래스를 어느 폴더에 만들지 지정 |
Q클래스?
JPA 엔티티를 기반으로 QueryDSL이 빌드 타임에 생성하는 정적 메타 모델 클래스
쿼리에서 문자열 대신 타입 기반으로 안전하게 쿼리를 작성할 수 있게 해줌.
✅QueryDSL은 타입 기반이라 IDE 자동완성 + 컴파일 오류 잡힘
QAnimalStats stats = QAnimalStats.animalStats;
queryFactory.selectFrom(stats)
.where(stats.animalType.id.eq(1L)) // 자동완성 + 안전함
.fetch();
QueryDSL 사용 준비
@Configuration
public class QuerydslConfiguration {
@PersistenceContext
private EntityManager entityManager;
@Bean
public JPAQueryFactory jpaQueryFactory() {
return new JPAQueryFactory(entityManager);
}
}
QuerydslConfiguration 만 추가하면 끝이다.
원래는 repository 를 상속받고 구현해야하는 작업이 복잡한데
향로님 블로그 참고해서 querydsl로만 구현할 것이다.
먼저 프로젝트를 빌드해서 Q클래스들을 생성한다.
@Entity를 사용해서 DTO를 잘 만들었다면 잘 생성된다.
QueryRepository
@RequiredArgsConstructor
@Repository
public class AnimalStatsQueryRepository {
private final JPAQueryFactory queryFactory;
public List<RegionTopAnimalTypeResponse> getTopAnimalTypeResponseList(RegionTopAnimalTypeRequest request) {
List<RegionTopAnimalTypeResponse> dogTop10 = getTopBreedsBySpecies(request.getRegionId(), 1);
List<RegionTopAnimalTypeResponse> catTop10 = getTopBreedsBySpecies(request.getRegionId(), 2);
List<RegionTopAnimalTypeResponse> result = new ArrayList<>();
result.addAll(dogTop10);
result.addAll(catTop10);
return result;
}
private List<RegionTopAnimalTypeResponse> getTopBreedsBySpecies(Integer regionId, Integer speciesId) {
QAnimalStats stats = QAnimalStats.animalStats;
QAnimalType type = QAnimalType.animalType;
QSpecies species = QSpecies.species;
return queryFactory
.select(Projections.constructor(
RegionTopAnimalTypeResponse.class,
species.name,
type.name,
stats.animalCount.sum()
))
.from(stats)
.join(stats.animalType, type)
.join(type.species, species)
.where(
regionId != null ? stats.region.id.eq(regionId) : null,
species.id.eq(speciesId)
)
.groupBy(type.id, species.name, type.name)
.orderBy(stats.animalCount.sum().desc())
.limit(10)
.fetch();
}
}
조인쿼리가 많아서 좀 복잡한데, 지피티한테 내가 작성한 쿼리를 기반으로 querydsl 작성해달라고 요청했다.
species_name 은 빼먹었는데, 찰떡같이 알아듣고 추가해줬다.
(소스코드 내에 Q클래스를 찾지 못해서 이것저것해봤는데, clean restart build 몇 번 하니까 됨;;)
Service & Controller
@RequiredArgsConstructor
@Service
public class AnimalTypeService {
private final AnimalStatsQueryRepository animalStatsQueryRepository;
public List<RegionTopAnimalTypeResponse> getTopAnimalTypes(RegionTopAnimalTypeRequest request) {
return animalStatsQueryRepository.getTopAnimalTypeResponseList(request);
}
}
@RequiredArgsConstructor
@RestController
public class AnimalTypeController {
private final AnimalTypeService animalTypeService;
@GetMapping("/api/top-animal-type")
public List<RegionTopAnimalTypeResponse> getTopAnimalType(RegionTopAnimalTypeRequest regionTopAnimalTypeRequest) {
return animalTypeService.getTopAnimalTypes(regionTopAnimalTypeRequest);
}
}
서비스와 컨트롤러는 테스트용도로 간단하게 작성했다.
수정한다고해도 path 외에는 크게 변할 건 없어 보인다.
테스트
잘 나온다!
쿼리하기까지 우여곡절이 많을 줄 알았는데 생각보다 바로 됐다.
프론트 할 시간이 생각보다 빠르게 다가와서 겁난다 ^^;;
'프로젝트 > 반려동물' 카테고리의 다른 글
[반려동물 프로젝트] 마무리 (1) | 2025.04.26 |
---|---|
[반려동물 프로젝트] 백엔드 수정 (SpringCache) (3) | 2025.04.25 |
[반려동물 프로젝트] 프론트 구현 (Mustache) (0) | 2025.04.25 |
[반려동물 프로젝트] DB 구성 (JPA, Python) (0) | 2025.04.24 |
[반려동물 프로젝트] 데이터 활용 고민 (0) | 2025.04.24 |