pom.xml 에 의존성과 플러그인을 추가한다.
querydsl-jpa: QueryDSL JPA 라이브러리
querydsl-apt: 쿼리 타입(Q)을 생성할 때 필요한 라이브러리
<!-- <https://mvnrepository.com/artifact/com.querydsl/querydsl-jpa> -->
<!-- ${querydsl.vesion 은 spring boot 에 내장 설정 되어있다.-->
<!-- QueryDSL APT Config -->
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>${querydsl.version}</version>
<scope>provided</scope>
</dependency>
<!-- QueryDSL JPA Config -->
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId>
<version>${querydsl.version}</version>
</dependency>
QueryDSL을 사용하려면 Criteria의 메타 모델처럼 엔티티를 기반으로 쿼리 타입이라는 쿼리용 클래스를 생성해야 한다.
다음과 같이 쿼리 타입 생성용 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>
</configuration>
</execution>
</executions>
</plugin>
maven clean, install 후 target/generated-sources/java 경로에 Query 타입이 자동생성된다.
intall 시에 @Entity 로 선언한 DTO 의 Query 타입이 생성된다.
매번 생성하지 않아도 사용할 수 있도록 JPAQueryFactory 객체를 Bean으로 등록한다.
QueryDslConfig.java
@Configuration
public class QueryDslConfig {
@PersistenceContext
private EntityManager entityManager;
@Bean
public JPAQueryFactory jpaQueryFactory() {
return new JPAQueryFactory(entityManager);
}
}
UserDTO.java @Entity를 가지고 QUserDTO.java 라는 Query 타입이 생성된다.
EntityManager 를 JPAQueryFactory에 넣고, QUserDTO 객체를 가지고 아래와 같은 방식으로 쿼리를 코드로 짤 수 있다.
UserDTO
@Entity
@Table(name = "USERS")
public class UserDTO {
@Id
@Column(name="id")
private String id;
@Column(name="name")
private String name;
@Column(name="age")
private int age;
}
test code
@Test
@DisplayName("유저 select 테스트")
public void readUser() {
//given
QUserDTO qUser = QUserDTO.userDTO; // 기본 인스턴스로 사용
// QUserDTO qUser = new QUserDTO("myQUser"); 별칭으로 생성
//when
var result = query.selectFrom(qUser)
.where(qUser.name.like("%test%"))
.fetch();
//then
assertThat(result.size(), is(2));
}
주의!
Query 타입 객체를 선언할때, 기본적으로는 인스턴스로 사용해도 상관없지만,
같은 엔티티를 조인하거나 같은 엔티티를 서브쿼리에 사용할 때는 별도의 별칭으로 생성 해야한다.
Querydsl 의 장점중 하나
조건문을 아주 쉽고 빠르게 추가/수정 할 수 있다.
나이가 20 이상인 김씨 user만 select 한다고 가정한다면.
int ageMin = 20;
var result = query.selectFrom(qUser)
.where(qUser.age.gt(ageMin)
,qUser.userName.startsWith("kim"))
.fetch();
단순하게 위처럼 조건문을 작성하는 대신
BooleanBuilder 혹은 BooleanExpression 을 반환하는 함수를 사용하여, 가독성과 재사용성을 높일 수 있다.
public BooleanExpression ageMin(int ageMin){
return QUserDTO.userDTO.age.gt(ageMin);
}
...
int ageMin = 20;
var result = query
.selectFrom(qUser)
.where(ageMin(ageMin)
,qUser.userName.startsWith("kim"))
.fetch();
/* ------------------------------------------------------------------------- */
public BooleanExpression filterByAgeMinAndFamilyName(int ageMin, String fName){
return QUserDTO.userDTO.userName.startsWith(fName)
.and(ageMin(ageMin));
}
...
String fName ="lee";
ageMin = 10;
var result = query
.selectFrom(qUser)
.where(filterByAgeMinAndFamilyName(ageMin, fName))
.fetch();
// 같은 엔티티 조인이나, 같은 엔티티의 서브쿼리일 경우, 따로 parameter로 Query 타입을 넘겨주는게 안전하다.