정적 타입을 이용해 SQL과 같은 쿼리를 생성할 수 있게 지원하는 프레임워크
문자열이나 XML파일을 통해 쿼리를 작성하는 대신 QueryDSL이 제공하는 Fluent API
를 활용해 쿼리 작성 가능
동적으로 쿼리 생성 가능
가독성
+ 생산성
향상의존성 추가
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId>
</dependency>
< plugins > 태그에 APT 플러그인 추가
<plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>apt-maven-plugin</artifactId>
<version>1.1.3</version>
<executions>
<execution>
<goals>
<goal>process</goal>
</goals>
<configuration>
<outputDirectory>target/generated-sources/java</outputDirectory>
<processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
<options>
<querydsl.entityAccessors>true</querydsl.entityAccessors>
</options>
</configuration>
</execution>
</executions>
</plugin>
JPAAnnotationProcessor은 @Entity 어노테이션으로 정의된 엔티티 클래스를 찾아서 쿼리 타입 생성
이후 maven lifecycle -> compile 단계 클릭해 빌드 수행
그러면 Q 도메인 클래스가 생성된 것을 볼 수 있다
QueryDSL은 지금까지 작성했었던 엔티티 클래스
와 QDomain
이라는 쿼리 타입의 클래스를 자체적으로 생성해서 메타데이터로 사용
-> 이를 통해 SQL과 같은 쿼리를 생성하여 제공
generated-sources를 Mark as Sources
로 하여 IDE에서 소스 파일로 인식할 수 있게 해주어야 함
@PersistenceContext
EntityManager entityManager;
@Test
void queryDslTest(){
JPAQuery<Product> query = new JPAQuery(entityManager);
QProduct qProduct = QProduct.product;
List<Product> productList = query
.from(qProduct)
.where(qProduct.name.eq("펜"))
.orderBy(qProduct.price.asc())
.fetch();
for(Product product : productList){
System.out.println("-----------------");
System.out.println();
System.out.println("Product Number : " + product.getNumber());
System.out.println("Product Name : " + product.getName());
System.out.println("Product Price :" + product.getPrice());
System.out.println("Product Stock :" + product.getStock());
System.out.println();
System.out.println("-------------------");
}
}
위 코드는 QueryDSL에 의해 생성된 Q도메인 클래스를 활용하는 코드
Q도메인 클래스와 대응되는 테스트 클래스가 없으므로 엔티티 클래스에 대응되는 리포지토리의 테스트 클래스에 포함해도 무관
QueryDSL을 사용하기 위해선 JPAQuery 객체를 사용
-> EntityManager
를 활용하여 생성
생성된 JPAQuery는 9~13번 줄 같이 빌더 형식으로 쿼리 작성
List로 반환 받기 위해서는 fetch() 메서드를 사용해야 함
List< T > fetch()
: 조회 결과를 리스트로 반환T fetchOne
: 단 한건의 조회 결과 반환T fetchFirst()
: 여러 건의 조회 결과중 1건을 반환Long fetchCount()
: 조회 결과의 개수를 반환QueryResult< T > fetchResults()
: 조회 결과 리스트와 개수를 포함한 QueryResults 반환QueryDSL을 사용하기 위해 JPAQueryFactory
를 사용하는 경우도 있음
JPAQueryFactory를 활용한 QueryDSL 테스트 코드
@Test
void queryDslTest2(){
JPAQueryFactory jpaQueryFactory = new JPAQueryFactory(entityManager);
QProduct qProduct = QProduct.product;
List<Product> productList = jpaQueryFactory.selectFrom(qProduct)
.where(qProduct.name.eq("펜"))
.orderBy(qProduct.price.asc())
.fetch();
for(Product product : productList){
System.out.println("-----------------");
System.out.println();
System.out.println("Product Number : " + product.getNumber());
System.out.println("Product Name : " + product.getName());
System.out.println("Product Price :" + product.getPrice());
System.out.println("Product Stock :" + product.getStock());
System.out.println();
System.out.println("-------------------");
}
}
JPAQuery를 사용했을 때와 달리 JPAQueryFactory에서는 select 절부터 가능
일부만 조회하고 싶다면 selectFrom()이 아닌 select()와 From() 메서드 구분해서 사용하자
@Test
void queryDslTest3(){
JPAQueryFactory jpaQueryFactory = new JPAQueryFactory(entityManager);
QProduct qProduct = QProduct.product;
List<String> productList = jpaQueryFactory
.select(qProduct.name)
.from(qProduct)
.where(qProduct.name.eq("펜"))
.orderBy(qProduct.price.asc())
.fetch();
for(String product : productList){
System.out.println("-------------------");
System.out.println("Product Name : " + product);
System.out.println("------------------");
}
List<Tuple> tupleList = jpaQueryFactory
.select(qProduct.name, qProduct.price)
.from(qProduct)
.where(qProduct.name.eq("펜"))
.orderBy(qProduct.price.asc())
.fetch();
for(Tuple product : tupleList){
System.out.println("----------");
System.out.println("Product Name :" + product.get(qProduct.name));
System.out.println("Product Name :" + product.get(qProduct.price));
System.out.println("----------");
}
}
productList는 select 대상이 하나
인 경우
TupleList처럼 select 대상이 여러개
인 경우 쉼표로 구분해서 작성하면 되고, 리턴 타입을 List< tuple > 로 작성
컨피그 클래스 생성
QueryDSLConfig.java
@Configuration
public class QueryDSLConfiguration {
@PersistenceContext
EntityManager entityManager;
@Bean
public JPAQueryFactory jpaQueryFactory(){
return new JPAQueryFactory(entityManager);
}
}
위와 같이 JPAQueryFactory 객체를 @Bean 객체로 등록시 앞선 방법처럼 JPAQueryFactory를 초기화 하지 않고 스프링 컨테이너에서 가져다 쓸 수 있음
ProductRepositoryTest
@Autowired
JPAQueryFactory jpaQueryFactory;
@Test
void queryDslTest4(){
QProduct qProduct = QProduct.product;
List<String> productList = jpaQueryFactory
.select(qProduct.name)
.from(qProduct)
.where(qProduct.name.eq("펜"))
.orderBy(qProduct.price.asc())
.fetch();
for(String product : productList){
System.out.println("-----------------");
System.out.println("Product Name : " + product);
System.out.println("-----------------");
}
}
Hibernate:
select
product0_.name as col_0_0_
from
product product0_
where
product0_.name=?
order by
product0_.price asc
-----------------
Product Name : 펜
-----------------
-----------------
Product Name : 펜
-----------------
-----------------
Product Name : 펜
-----------------