728x90
반응형
728x90
Querydsl
- Java 기반의 타입 안전한 쿼리 DSL(Domain Specific Language)로 데이터베이스에 대한 쿼리를 객체 지향적으로 작성할 수 있게 해주는 도구
- 다양한 데이터 소스와 통합되어 사용되며 개발자들이 SQL 쿼리를 문자열로 작성하는 대신 타입 안전한 방법으로 쿼리를 생성하고 실행할 수 있도록 도와줌
주요 특징
- 타입 안전성: 컴파일 타임에 쿼리를 검증하여 잘못된 필드 이름이나 타입에 대한 에러를 예방하고 런타임 에러 발생을 줄임
- 가독성: SQL 쿼리를 자바 코드 형식으로 작성할 수 있어 가독성이 높음
- 유연성: 다양한 데이터베이스와 ORM 프레임워크와 호환되며 SQL 뿐만 아니라 JPA, MongoDB, Lucene 등 다양한 쿼리 타입을 지원
설정 방법
- 개별적인 설정이 필요
//Querydsl 5.0 버전 기준
//build.gradle 의존성 추가
dependencies {
//Querydsl JPA 모듈 추가
implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
//Querydsl의 애너테이션 프로세서(APT)를 추가 -> Q타입 엔티티 생성, 외부 설정에서 지정한 Querydsl 버전을 참조하여 버전 관리
annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta"
//Jakarta EE에서 제공하는 어노테이션 API를 포함 -> 어노테이션 기반 기능 구현 시 사용
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
//JPA 관련 애너테이션을 처리
annotationProcessor "jakarta.persistence:jakarta.persistence-api"
}
사용 방법
- JPA 엔티티 클래스를 기반으로 Q타입 클래스를 생성
- EntityManager를 통해 JPAQueryFactory 생성
- 생성된 JPAQueryManager와 Q타입 엔티티를 통해 쿼리 생성
Q-Type class
- Querydsl에서 생성하는 메타 모델 클래스
- JPA 엔티티의 필드에 대한 정보를 담고 있음
- JPA 엔티티 클래스를 기반으로 자동으로 생성 -> 1대 1 대응 (예: User -> QUser)
Q-Type class 생성 과정
- 빌드 도구가 컴파일 수행 시 Querydsl 어노테이션 프로세서 동작
- JPA @Entity 어노테이션이 붙은 클래스 식별(@QueryEntity 클래스도 처리 대상)
- 식별된 엔티티 클래스에 해당하는 Q-Type 클래스 생성 -> 생성된 클래스는 보통 'target/generated-sources/java' 또는 'build/generated/sources/annotationProcessor/java/main' 디렉터리에 저장
- 원본 엔티티의 각 필드에 대해 Q-Type 클래스에 해당하는 필드 생성 -> 쿼리 작성 시 사용
- 생성된 클래스를 사용하여 쿼리 작
Q-Type 사용법
- Q-Type 객체 내부 원본 객체 이름의 맨 앞 글자를 소문자로 바꾼 정적 인스턴스 생성
- new 명령어를 통해 별칭을 주어 생성 가능
- 같은 객체를 조인할 경우 구분을 위해 별칭으로 생성하여 사용
//기본 인스턴스를 사용하는 방법
QUser qUser = QUser.user;
//별칭 생성
QUser qUser = new QUser("qu");
JPAQueryFactory
- 쿼리를 생성하고 실행하는 데 사용하는 클래스
- SQL과 유사한 이름의 메서드를 제공하여 메서드 체인을 통해 사용
//예시
JPAQueryFactory queryFactory = new JPAQueryFactory(entityManager);
User findUser = queryFactory
.select(QUser.user)
.From(QUser.user)
.where(QUser.user.name.eq("park")
.and(QUser.user.age.eq(10)))
.fetchOne();
주요 메서드
- select(): JPQL의 select절
- selectFrom(): JPQL의 select와 from에 동일한 객체를 사용
- from(): JPQL의 from절
- where(): JPQL의 where절
- fetch(): 쿼리를 실행하고 리스트 형태로 반환 -> EntityManager의 getResultList()
- fetchOne(): 쿼리를 실행하고 단일 결과를 반환 -> EntityManager의 getSingleResult()
- fetchFirst(): 쿼리를 실행하고 첫 번째 결과를 반환
- orderBy(): JPQL의 order by 절
- offset(): 결과의 시작 위치 지정 -> 페이징
- limit(): 반환할 결과의 최대 개수 지정
- join(): JPQL의 조인 절
- fetchJoin(): JPQL fetch join 사용 가능
- groupBy(): JPQL의 group by 절
//예시 - join
List<User> usersInGroup = queryFactory
.select(QUser.user)
.from(QUser.user)
.join(QUser.user.group)
.where(QGroup.group.name.eq("admin"))
.fetch();
//예시 - AND, OR
List<User> users = queryFactory
.select(QUser.user)
.from(QUser.user)
.where(QUser.user.age.goe(18)
.and(QGroup.group.name.eq("member"))
.or(QUser.user.name.eq("admin")))
.fetch();
//예시 - paging, sorting
List<User> users = queryFactory
.select(QUser.user)
.from(QUser.user)
.join(QUser.user.group)
.where(QGroup.group.name.eq("member"))
.orderBy(QUser.user.age.asc())
.limit(10)
.fetch();
//예시 - 집합
List<Tuple> averageAges = queryFactory
.select(QGroup.group.name, QUser.user.age.avg())
.from(QUser.user)
.join(QUser.user.group)
.groupBy(QGroup.group.name)
.fetch();
//예시 - fetch join
List<User> usersWithGroup = queryFactory
.selectFrom(QUser.user)
.join(QUser.user.group).fetchJoin()
.where(QGroup.group.name.eq("admin"))
.fetch();
검색 조건 지정
user.name.eq("park") : = equal
user.name.ne("park") : != not equal
user.name.eq("park").not() : ne와 동일
user.name.isNotNull : != null
user.age.in(10, 20) : age in(10, 20)
user.age.notIn(10, 20) : age not in(10, 20)
user.age.between(10, 20) : age between(10, 20)
user.age.goe(30) : age >= 30 Greater than or equal to
user.age.gt(30) : age > 30 Greater than
user.age.loe(30) : age <= 30 Less than or equal to
user.age.lt(30) : age < 30 Less than
user.name.like("kim%") : like 조건 검색
user.name.contains("kim") : %kim% 조건 검색
user.name.startsWith("kim") : kim% 조건 검색
Case 문
- 복잡한 조건을 가진 case문 사용 가능
//단순 조건 case
List<String> result = queryFactory
.select(QUser.user.age.intValue()
.when(10).then("열살")
.when(20).then("스무살")
.otherwise("기타"))
.from(QUser.user)
.fetch();
//CaseBuilder 사용 복잡한 조건
List<String> result = queryFactory
.select(new CaseBuilder()
.when(QUser.user.age.intValue().between(10, 19)).then("10대")
.when(QUser.user.age.intValue().between(20, 29)).then("20대")
.otherwise("기타"))
.from(QUser.user)
.fetch();
상수 & 문자 더하기
- select 절에서 Expressions.constant()를 사용하여 상수를 더 할 수 있음
- concat을 사용해 문자 더하기가 가능 -> 숫자일 경우 stringValue()를 사용하여 문자로 변환
//상수 더하기 Expressions
Tuple result = queryFactory
.select(QUser.user.name, Expressions.constant("A"))
.from(QUser.user)
.fetchFirst()
//문자 더하기 concat()
String result = queryFactory
.select(QUser.user.name.concat("_").concat(QUser.user.age.stringValue()))
.from(QUser.user)
.where(QUser.user.name.eq("park"))
.fetchOne();
Tuple
- 여러 개의 값(컬럼)을 그룹화하여 반환할 수 있는 데이터 구조
- 쿼리 결과에서 여러 필드를 동시에 가져와야 할 때 사용
- Tuple 객체에서 값에 접근 시 인덱스 또는 이름 사용 가능
- 집계 쿼리나 다양한 쿼리와 조합 가능
//인덱스 접근
String userName = tuple.get(0, String.class); // 첫 번째 값 (사용자 이름)
String groupName = tuple.get(1, String.class); // 두 번째 값 (그룹 이름)
//이름 접근
String userName = tuple.get(QUser.user.name);
String groupName = tuple.get(QGroup.group.name);
//집계 쿼리 사용 시
String maxAge = tuple.get(QUser.user.age.max());
실전! Querydsl 강의 | 김영한 - 인프런 (inflearn.com) 강의 내용 참고
728x90
'백엔드 > Spring' 카테고리의 다른 글
[Spring] 스프링 부트 (2) | 2024.09.27 |
---|---|
[Spring] Querydsl DTO & 동적 쿼리 (1) | 2024.09.26 |
[Spring] Spring Data Jpa 확장 기능 (0) | 2024.09.24 |
[Spring] 쿼리 메서드 (0) | 2024.09.23 |
[Spring] Spring Data JPA (2) | 2024.09.22 |