QueryDSL
Java에서 타입 세이프한 쿼리 생성을 위해 사용하는 라이브러리 입니다. Query DSL 을 사용하면 JPA Criteria API나 JPQL로 작성하던 쿼리를 코드로 더 작관적으로 표현할 수 있습니다.
- 장점과 단점
장점
- 타입 세이프티: QueryDSL은 컴파일 시점에 타입 체크를 통해 잘못된 쿼리를 방지할 수 있습니다.
- 가독성: 메서드 체인 방식으로서 조건을 여러 줄에 걸쳐 작성할 수 있습니다. 복잡한 조건을 표현하기 좋습니다.
- 재사용성: booleanExpression 을 사용하여 and, or 조건으로 조합하여 사용할 수 있습니다.
- 자동 완성 지원: IDE에서 자동 완성 기능을 제공해 쿼리 작성이 더 편리해집니다.
- 컴파일 시점에서 에러를 확인할 수 있습니다.
단점
- 빌드 시간 증가: 빌드 시간이 길어질 수 있습니다. Q 클래스 생성을 위해 APT(Annotation Processing Tool)를 사용하는데, 이로 인해 JAR 파일로 만드는 과정에서 빌드 시간이 다소 길어질 수 있습니다.
- 외부 의존성: QueryDSL은 외부 라이브러리이며 초기 설정이 복잡할 수 있습니다.
- 단순한 쿼리가 필요할 때는 JPA나 JPQL 에는 쓰는 게 더 낫습니다.
- 사용법
1. 의존성 추가
dependencies {
implementation "com.querydsl:querydsl-jpa:${querydslVersion}:jakarta"
kapt "com.querydsl:querydsl-apt:${querydslVersion}:jakarta"
annotationProcessor "com.querydsl:querydsl-apt:${querydslVersion}:jakarta"
}
2. Q 클래스 생성
QueryDSL 을 사용하면 각 엔티티에 대한 Q 클래스가 자동 생성됩니다. 예를 들어 User 엔티티가 있을 때, User 엔티티에 대한 쿼리를 작성하려면 QUser 라는 클래스를 사용하게 됩니다. Q 클래스는 build/generated 디렉토리에 생성됩니다. 프로젝트 설정에 따라 IDE에서 해당 파일을 빌드 경로로 추가해줘야 할 수 있습니다.
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private int age;
// getters and setters
}
이러한 User 엔티티가 있을 때, 이 엔티티에 대해 아래와 같은 QUser 클래스가 생성됩니다. QUser 클래스는 User 엔티티의 모든 필드를 static 필드로 정의하여 쿼리 작성 시 타입 세이프하게 접근할 수 있게 해줍니다.
// QUser.java (자동 생성된 클래스)
public class QUser extends EntityPathBase<User> {
public static final QUser user = new QUser("user");
public final StringPath name = createString("name");
public final NumberPath<Integer> age = createNumber("age", Integer.class);
public QUser(String variable) {
super(User.class, forVariable(variable));
}
}
- QUser.user는 User 엔티티에 대한 기본 인스턴스로, name과 age 같은 필드에 타입 세이프하게 접근할 수 있습니다.
3. QueryDsl Configuration 설정
JPAQueryFactory 빈을 설정하기 위한 Configuration 클래스를 작성합니다.
import com.querydsl.jpa.impl.JPAQueryFactory;
import jakarta.persistence.EntityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class QuerydslConfig {
private final EntityManager entityManager;
public QuerydslConfig(EntityManager entityManager) {
this.entityManager = entityManager;
}
@Bean
public JPAQueryFactory jpaQueryFactory() {
return new JPAQueryFactory(entityManager);
}
}
- JPAQueryFactory는 EntityManager를 주입 받아 생성됩니다.
- 이 빈을 통해 다른 클래스에서 QueryDSL을 활용하여 쿼리를 작성할 수 있습니다.
- 하나의 레포지토리에서만 QueryDSL을 사용하고자 한다면 객체로 생성하는 방법도 있습니다.
4. interface RepositoryQuery 작성
import com.example.domain.User;
import java.util.List;
public interface UserRepositoryQuery {
List<User> findUsersByName(String name);
}
5. RepositoryQueryImpl : 위 레포지토리 구현체 클래스 작성
import com.example.domain.QUser;
import com.example.domain.User;
import com.querydsl.jpa.impl.JPAQueryFactory;
import org.springframework.stereotype.Repository;
import java.util.List;
public class UserRepositoryQueryImpl implements UserRepository {
private final JPAQueryFactory queryFactory;
private final QUser user = QUser.user;
public UserRepositoryImpl(JPAQueryFactory queryFactory) {
this.queryFactory = queryFactory;
}
@Override
public List<User> findUsersByName(String name) {
return queryFactory
.selectFrom(user)
.where(user.name.eq(name))
.fetch();
}
}
6. 일반적으로 UserService 는 JpaRepository 와 커스컴 Repository를 동시에 상속 받은 UserRepository 를 주입 받습니다.
public interface UserRepository extends JpaRepository<User, Long>, UserRepositoryQuery {
//...
}
@Service
public class UserService {
private final UserRepository userRepository;
// ...
}
- 동적 쿼리 작성 예시
- name과 age 조건이 선택적으로 추가되는 User 조회 쿼리
1. BooleanBuilder
@Override
public List<User> findUsers(String name, Integer age) {
BooleanBuilder builder = new BooleanBuilder();
if (name != null) {
builder.and(user.name.eq(name));
}
if (age != null) {
builder.and(user.age.eq(age));
}
return queryFactory
.selectFrom(user)
.where(builder)
.fetch();
}
2. BooleanExpression
@Override
public List<User> findUsers(String name, Integer age) {
return queryFactory
.selectFrom(user)
.where(nameEq(name), ageEq(age)) // 매개변수, 조건들을 조합
.fetch();
}
// 개별 조건 메서드들 정의
private BooleanExpression nameEq(String name) {
return name != null ? user.name.eq(name) : null;
}
private BooleanExpression ageEq(Integer age) {
return age != null ? user.age.eq(age) : null;
}
'Spring > Spring 문법' 카테고리의 다른 글
QueryDsl - Projections 의 4가지 방식 (0) | 2024.11.21 |
---|---|
Spring Security 적용 (0) | 2024.11.14 |
@RequestParam 에서 매개 변수에 null 넣기 (0) | 2024.11.14 |
Java Spring properties (1) | 2024.10.26 |
Spring boot 에서 이메일 인증 구현하기 (googleEmail) - Bean 만들어서 주입하기 (0) | 2024.10.24 |