티스토리 뷰
이전 글에서는 QueryDSL을 왜 도입하려했는지, 왜 선택했는지 다른 툴과 비교 분석을 해봤다.
이번 글은 스프링부트에 어떻게 설치하고 사용할지 작성해보려 한다.
build.gradle
plugins {
id 'com.ewerk.gradle.plugins.querydsl' version '1.0.10'
...
}
dependencies {
implementation "com.querydsl:querydsl-jpa:5.0.0"
implementation "com.querydsl:querydsl-apt:5.0.0"
...
}
def querydslDir = "$buildDir/generated/querydsl"
querydsl {
jpa = true
querydslSourcesDir = querydslDir
}
sourceSets {
main.java.srcDir querydslDir
}
compileQuerydsl{
options.annotationProcessorPath = configurations.querydsl
}
tasks.withType(JavaCompile) {
options.encoding = 'UTF-8'
}
sourceSets {
main.java.srcDir querydslDir
}
gradle 설정 방법이 조금 까다롭다. Q 클래스를 생성하고 사용하기 위해서 경로를 지정해줘야 하기 때문이다.
그래도 JOOQ 설정 방법보다는 낫다.
위와 같이 설정 하면
def querydslDir = "$buildDir/generated/querydsl" 로 지정한 디렉토리 아래에 Q클래스들이 생성된다.
Q클래스들은 @Entity라고 지정한 테이블 정보를 가지고 있는 클래스에 매핑되어 생성된다.
이제 이 Q클래스를 사용하고 위해 interface와 구현체를 생성해야 한다.
이 interface를 독립적인 repository로 사용하는 법과 Spring Data JPA 인터페이스와 함께 사용하는 법이 있는데,
spring Data JPA 인터페이스와 함께 사용하는 법으로 작성해보고자 한다.
실제로 이렇게 구현해서 사용했고, 가독성과 퍼포먼스를 모두 잡을 수 있었다.
이 기능 때문에 Query DSL을 선택했기도 하다.
Spring Data JPA 인터페이스와 함께 사용하기
UserRepository가 User에 대한 정보를 담고 있는 Spring Data JPA 인터페이스이다.
쿼리 dsl 디렉토리 아래에 Custom suffix를 달고 있는 UserRepositoryCustom 인터페이스를 생성해준다.
(디렉토리 구조는 임의로 지정해도 상관없다.)
public interface UserRepositoryCustom {
List<User> findByUserTest();
}
별다른 내용은 담지 않았고, 테스트용 매서드를 하나 생성했다.
@RequiredArgsConstructor
public class UserRepositoryCustomImpl implements UserRepositoryCustom {
private final JPAQueryFactory jpaQueryFactory;
@Override
public List<User> findByUserTest() {
QUser user = QUser.user;
return jpaQueryFactory.selectFrom(user)
.where(user.userId.eq("이 아이디를 찾고 싶어"))
.fetch();
}
}
Q파일을 글로벌하게 작성하고 싶다면 아래와 같이 정의하고 써도 된다.
import static com.[프로젝트명].api.user.models.entities.QUser.user;
구현체에서 Custom 인터페이스를 implements 받아서 구현한다.
이 커스텀 구현체를 UserRepsitory에 JpaRepository와 함께 implements 받아서 사용하면 된다.
@Repository
public interface UserRepository extends JpaRepository<User, Integer>, UserRepositoryCustom {
List<User> findAll();
User findByUserId(String userId);
// 추가 구현
}
이렇게되면, 서비스에서는 별도의 QueryDSL 인터페이스를 정의하지 않고 UserRepository 하나만 정의해도
QueryDSL로 구현한 매서드를 가져다 쓸 수 있게 된다.
@RequiredArgsConstructor
@Service
public class UserService {
private final UserRepository userRepository;
public List<User> getTestUser() {
return userRepository.findByUserTest(); // userRepository에서 바로 가져다 쓸 수 있음
}
}
코드를 예시를 들기 위해 간단하게 작성했지만, 사실상 일반 쿼리랑 다를바 없이 where 절을 작성할 수 있다.
member.username.eq("member1") // username = 'member1'
member.username.ne("member1") //username != 'member1'
member.username.eq("member1").not() // username != 'member1'
member.username.isNotNull() //이름이 is not null
member.age.in(10, 20) // age in (10,20)
member.age.notIn(10, 20) // age not in (10, 20)
member.age.between(10,30) //between 10, 30
member.age.goe(30) // age >= 30
member.age.gt(30) // age > 30
member.age.loe(30) // age <= 30
member.age.lt(30) // age < 30
member.username.like("member%") //like 검색
member.username.contains("member") // like ‘%member%’ 검색
member.username.startsWith("member") //like ‘member%’ 검색
또한 Tuple, Projection을 활용해 alias된 컬럼명들이나 데이터의 일부만 매핑할 수 있는 기능도 제공한다.
Tuple
public List<Tuple> getUsersWithNameAndEmail() {
QUser user = QUser.user;
return queryFactory.select(user.name, user.email)
.from(user)
.fetch();
}
// 사용 예시
List<Tuple> userTuples = userRepository.getUsersWithNameAndEmail();
for (Tuple tuple : userTuples) {
String name = tuple.get(0, String.class);
String email = tuple.get(1, String.class);
System.out.println("Name: " + name + ", Email: " + email);
}
Projection
public List<UserNameAndEmailProjection> getUsersWithNameAndEmail() {
QUser user = QUser.user;
return queryFactory.select(Projections.constructor(UserNameAndEmailProjection.class, user.name, user.email))
.from(user)
.fetch();
}
// 사용예시
List<UserNameAndEmailProjection> userProjections = userRepository.getUsersWithNameAndEmail();
for (UserNameAndEmailProjection projection : userProjections) {
String name = projection.getName();
String email = projection.getEmail();
System.out.println("Name: " + name + ", Email: " + email);
}
마치며
설정 방법이 복잡하고 배우기는 조금 어려울 수 있으나, 자바 코드 내에서 쿼리에 대한 모든 걸 해결할 수 있게 됐다.
이를 통해 생각보다 엄청난 퍼포먼스 향상을 이뤄낼 수 있었다.
가장 큰 장점은 코드 가독성의 희생이 없었다는 것이다.
이런 것들을 지표로 나타낼 수 없다는 게 참 아쉽다.
참고 사이트
1. [SpringBoot] Jpa Repository와 Querydsl 사용방법
2. [Querydsl] 6. Querydsl Where절
'개발 > DB' 카테고리의 다른 글
AWS RDS 블루-그린 배포로 MySQL 5.7 - 8.0 버전으로 업데이트하기 (0) | 2024.06.17 |
---|---|
MySQL Auto Increment 값 재설정하기 (1) | 2024.02.16 |
스프링부트에 QueryDSL 적용기 - 1 (Mybatis vs JPA vs JOOQ vs QueryDSL 비교) (0) | 2023.08.25 |
The MySQL server is running with the --read-only option so it cannot execute this statement 에러와 @Transactional (1) | 2023.06.01 |
[DB] 파티셔닝(Partitioning), 샤딩(Sharding) (0) | 2023.03.12 |
- Total
- Today
- Yesterday
- cache
- Log
- awskrug
- JWT
- springboot
- OpenFeign
- java
- AWS
- 인프런
- AOP
- Spring
- S3
- openAI API
- docker
- 스프링부트
- serverless
- Kotlin
- Elastic cloud
- chat GPT
- CloudFront
- GIT
- elasticsearch
- ChatGPT
- lambda
- terraform
- MySQL
- 람다
- OpenAI
- EKS
- AWS EC2
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |