상똥이의 Back-End 공부방
[Board Project] 12. API 구현 (Query DSL, Spring data, JPA) 본문
목차
1. query DSL을 사용해 필터 검색 기능 구현
[0. 현재 상황]
- 단순 데이터 서빙만 가능한 상황
- 필터 검색 불가능
- 즉, 구체적 검색 불가능하므로 이번에 구현할 것
[1. Query DSL 연결]
1. dependencies 내부 의존성 주입
(1) 필수 요소
implementation "com.querydsl:querydsl-jpa" //필수
implementation "com.querydsl:querydsl-core" //필수
annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}"
//버전을 자동으로 인식해줌
(2) Query DSL에 대응하기 위한 요소
implementation "com.querydsl:querydsl-collections"
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
annotationProcessor "jakarta.persistence:jakarta.persistence-api"
2.dependencies 외부 주입
(1) 주입 이유
- Query DSL은 자동으로 클래스를 생성하는 기능(Q-class)을 가지는데, 빌드 디렉토리 안에 들어가는 것을 밖으로꺼내기 위함
- 밖으로 꺼내는 이유 : 빌드할 때 발생할 잠재적인 문제 해결
- 잠재적인 문제 : gradle 빌드하며 스캔했던 영역과 intelliJ가 스캔하는 영역이 겹치면서 중복 발생
- 중복 = 충돌이므로 이 충돌문제를 해결하기 위해 Q-class의 위치를 강제로 옮긴 것
- 즉 없어도 되지만 intelliJ환경의 안정성을 위함
def generated = 'src/main/generated'
tasks.withType(JavaCompile) {
options.getGeneratedSourceOutputDirectory().set(file(generated))
}
sourceSets {
main.java.srcDirs += [generated]
}
clean {
delete file(generated)
}
[2. API 검색 기능 넣기]
1. extends QuerydslPredicateExequtor<테이블_클래스명>, QuerydslBinderCustomizer<Q테이블_클래스명>
(1) QuerydslPredicateExequtor<테이블_클래스명>
- 기본적으로 모든 필드에 대한 기본 검색 기능을 추가해줌
- exact match 검색만 가능 : 대소문자 구분은 안하지만, 부분 검색은 불가능함 (Ex. 키워드 검색이 불가능)
(2) QuerydslBinderCustomizer<Q테이블_클래스명>
- 오버라이드를 통해 구현된 내용을 토대로 검색에 대한 세부적인 규칙이 구성됨
- 오버라이드를 직접 구현하지 않고 JPA가 가능하게 해주기 때문에 default 메소드 사용
2. @Override -> customize
(1) bindings.excludeUnlistedProperties()
- PredicateExecutor에 의해 모든 필드들에 대한 검색이 열려있는데, 필터링을 걸어주는 역할
- 기본값이 false
(2) bindings.including() : 원하는 검색 조건을 설정할 수 있게 해줌, 괄호 안에 root.인덱스를 한 개 이상씩 넣어서 설정
(3) bindings.bind() : root.t인덱스를 하나씩 넣어 exact match검색 조건을 바꿀 수 있음
(4) first() : bindings.bind()에 붙여서 사용, 검색 타입을 설정할 수 있음
(5) StringExpression:: : 문자열 검색
- LikeIgnoreCase : 문자열이 다른 문자열 내에 순서에 상관 없이 포함되어있으면 검색됨
- containsIgnoreCase : 문자열이 다른 문자열 내에 순서대로 포함되어있으면 검색됨, 와일드카드(%) 사용
(6) DateTimeExpression:: : 날짜와 시간 검색
- eq : 정확히 같은 내용을 검색 (*잠시만 이대로 해놓고 후에 수정)
[3. API 검색]
1. Article에서 내용(content) 검색
- 검색할 키워드 : Vivamus metus
검색 방법 | 키워드 그대로 | 대소문자 바꿔서 | 순서 바꿔서 |
검색 문자 | http://localhost:8080/api/articles?content=Vivamus%20metus | http://localhost:8080/api/articles?content=vivAmus%20mETus | http://localhost:8080/api/articles?content=metus%20Vivamus |
결과 | 검색결과 같음 | 검색결과 같음 | 검색결과 다름 |
2. ArticleComments에서 내용(content) 검색
- 검색할 키워드 : pellentesque ultrices
검색 방법 | 키워드 그대로 | 대소문자 바꿔서 | 순서 바꿔서 |
검색 문자 | http://localhost:8080/api/articleComments?content=pellentesque%20ultrices | http://localhost:8080/api/articleComments?content=PeLLentesque%20ultriceS | http://localhost:8080/api/articleComments?content=ultrices%20pellentesque |
결과 | 검색결과 같음 | 검색결과 같음 | 검색결과 다름 |
package com.fastcampus.projectboard.repository;
import com.fastcampus.projectboard.domain.Article;
import com.fastcampus.projectboard.domain.QArticle;
import com.querydsl.core.types.dsl.DateTimeExpression;
import com.querydsl.core.types.dsl.StringExpression;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.data.querydsl.binding.QuerydslBinderCustomizer;
import org.springframework.data.querydsl.binding.QuerydslBindings;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
@RepositoryRestResource
public interface ArticleRepository extends
JpaRepository<Article, Long>,
QuerydslPredicateExecutor<Article>,
QuerydslBinderCustomizer<QArticle> {
@Override
default void customize(QuerydslBindings bindings, QArticle root){
bindings.excludeUnlistedProperties(true);
bindings.including(root.title, root.content, root.hashtag, root.createdAt, root.createdBy);
bindings.bind(root.title).first(StringExpression::containsIgnoreCase);
bindings.bind(root.content).first(StringExpression::containsIgnoreCase);
bindings.bind(root.hashtag).first(StringExpression::containsIgnoreCase);
bindings.bind(root.createdBy).first(StringExpression::containsIgnoreCase);
bindings.bind(root.createdAt).first(DateTimeExpression::eq);
}
}
package com.fastcampus.projectboard.repository;
import com.fastcampus.projectboard.domain.ArticleComment;
import com.fastcampus.projectboard.domain.QArticleComment;
import com.querydsl.core.types.dsl.DateTimeExpression;
import com.querydsl.core.types.dsl.StringExpression;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.data.querydsl.binding.QuerydslBinderCustomizer;
import org.springframework.data.querydsl.binding.QuerydslBindings;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
@RepositoryRestResource
public interface ArticleCommentRepository extends
JpaRepository<ArticleComment, Long>,
QuerydslPredicateExecutor<ArticleComment>,
QuerydslBinderCustomizer<QArticleComment> {
@Override
default void customize(QuerydslBindings bindings, QArticleComment root){
bindings.excludeUnlistedProperties(true);
bindings.including(root.content, root.createdAt, root.createdBy);
bindings.bind(root.content).first(StringExpression::containsIgnoreCase);
bindings.bind(root.createdBy).first(StringExpression::containsIgnoreCase);
bindings.bind(root.createdAt).first(DateTimeExpression::eq);
}
}
plugins {
id 'org.springframework.boot' version '2.7.0'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
group = 'com.fastcampus'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-data-rest'
implementation 'org.springframework.data:spring-data-rest-hal-explorer'
runtimeOnly 'com.h2database:h2'
runtimeOnly 'mysql:mysql-connector-java'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
implementation "com.querydsl:querydsl-jpa" //필수
implementation "com.querydsl:querydsl-core" //필수
implementation "com.querydsl:querydsl-collections"
annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jpa"
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
annotationProcessor "jakarta.persistence:jakarta.persistence-api"
}
tasks.named('test') {
useJUnitPlatform()
}
def generated = 'src/main/generated'
tasks.withType(JavaCompile) {
options.getGeneratedSourceOutputDirectory().set(file(generated))
}
sourceSets {
main.java.srcDirs += [generated]
}
clean {
delete file(generated)
}
'프로젝트 > 게시판 만들기' 카테고리의 다른 글
[Board Project] 14. 게시판 페이지 만들기(1) (Thymleaf) (0) | 2023.10.24 |
---|---|
[Board Project] 13. 뷰 엔드포인트 테스트 정의 (0) | 2023.10.23 |
[Board Project] 11. API 테스트 정의 (0) | 2023.10.16 |
[Board Project] 10. API 탐색 - 조회 위주 (HAL Explorer, Rest Repositories) (0) | 2023.10.15 |
[Board Project] 9. 데이터베이스 접근 로직 구현 (0) | 2023.10.12 |