QueryDSL 중급문법
실전! Querydsl - 인프런 | 강의
Querydsl의 기초부터 실무 활용까지, 한번에 해결해보세요!, - 강의 소개 | 인프런...
www.inflearn.com
문법 관련해서는 이미 정리가 잘된 강의자료가 있지만 SpringBoot + JPA + QueryDSL을 사용하여 토이프로젝트를 진행하기 전에 강의 내용을 한번 더 정리하고 진행하기 위해 해당 포스팅을 작성한다.
프로젝션과 결과 반환
- 프로젝션 대상이 하나면 반환 타입을 명확하게 지정할 수 있다.
- 프로젝션 대상이 둘 이상이면 반환 타입을 튜플 또는 DTO로 조회한다.
프로젝션 대상이 하나
자바 기본타입(String)으로 조회
List<String> result = queryFactory.select(member.username)
.from(member)
.fetch();
프로젝션 대상이 둘 이상
Tuple 타입으로 조회
List<Tuple> result = queryFactory.select(member.username, member.age)
.from(member)
.fetch();
DTO 타입으로 조회
DTO 타입으로 조회하는 3가지 방법
- 프로퍼티 접근(Setter 사용) - Projections.bean
- 필드 직접 접근 - Projections.fields
- 생성자 사용 - Projections.constructor생성자를 사용한 DTO 조회 예제
별칭(alias)을 지정하는 방법
- field.as("alias") 메서드를 통해 별칭을 지정하는 방법
- ExpressionUtils.as(JPAExpressions, "alias") 메서드를 통해 별칭을 지정하는 방법
첫 번째 인자로 JPAExpressions 객체를 이용하여 서브쿼리를 생성하고 두 번째 인자로 alias를 지정한다.
* ExpressionUtils은 Querydsl 내부에서 새로운 Expression을 사용할 수 있도록 지원하는 객체이다.
필드 직접 접근을 통한 DTO 타입 조회
List<MemberDTO> fetch = queryFactory.select(Projections.fields(MemberDTO.class,
member.username.as("name"),
ExpressionUtils.as(
JPAExpressions.select(
memberSub.age.max())
.from(memberSub), "age")))
.from(member)
.fetch();
@QueryProjection
DTO의 생성자에 @QueryProjection 어노테이션을 선언하여 프로젝션 결과를 반환 받을 수 있다.
장점
- 컴파일러로 타입을 체크할 수 있다
- 쿼리 실행코드가 간결해진다.
단점
- DTO에 QueryDSL어노테이션을 사용해야한다.
- DTO를 Q파일로 생성해야한다.
- DTO가 QueryDSL에 종속된다.
@QueryProjection 선언 예제
@Data
public class MemberDTO {
private String username;
private int age;
public MemberDTO() {}
@QueryProjection
public MemberDTO(String username, int age) {
this.username = username;
this.age = age;
}
}
@QueryProjection 쿼리 실행 예제
List<MemberDto> result = queryFactory.select(new QMemberDTO(member.username, member.age))
.from(member)
.fetch();
중복제거
JPQL과 동일하게 select절 뒤에 .distinct() 메서드를 작성해주면 된다.
List<String> result = queryFactory.select(member.username).distinct()
.from(member)
.fetch();
동적 쿼리
동적 쿼리를 작성하는 두가지 방법
- BooleanBuilder 사용
- Where 다중 파라미터 사용
BooleanBuilder 사용
String usernameParam = "member1";
Integer ageParam = 10;
BooleanBuilder builder = new BooleanBuilder();
if (usernameParam != null) builder.and(member.username.eq(usernameParam));
if (ageParam != null) builder.and(member.age.eq(ageParam));
List<Member> result = queryFactory.selectFrom(member).where(builder).fetch();
Where 다중 파라미터 사용
where절에서 다중 파라미터를 지정하게 되면 and조건으로 쿼리를 실행한다.
BooleanExpression을 반환 타입으로 하는 메서드를 선언하여 다중 파라미터로 지정하면 가독성 좋은 쿼리를 작성 가능하다.
특징
- where 조건에 null 값은 무시된다.
- 메서드를 다른 쿼리에서 재사용 할 수 있다.
private List<Member> searchMember(String usernameCond, Integer ageCond) {
return queryFactory.selectFrom(member)
.where(usernameEq(usernameCond), ageEq(ageCond))
.fetch();
}
private BooleanExpression usernameEq(String usernameCond) {
return usernameCond != null ? member.username.eq(usernameCond) : null;
}
private BooleanExpression ageEq(Integer ageCond) {
return ageCond != null ? member.age.eq(ageCond) : null;
}
수정, 삭제 벌크 연산
영속성 컨텍스트에 있는 엔티티를 무시하고 실행되기 때문에 벌크 연산 실행 후 영속성 컨텍스트를 초기화 하는 것이 안전하다.
long count = queryFactory.update(member)
.set(member.username, "비회원")
.where(member.age.lt(28))
.execute();
long count = queryFactory.delete(member)
.where(member.age.gt(18))
.execute();
SQL function 호출하기
SQL function은 JPA와 같이 Dialect에 등록된 내용만 호출할 수 있다.
Expressions.stringTemplate(String, Object ...) 메서드를 사용하여 쿼리를 작성하면 된다.
String result = queryFactory.select(Expressions
.stringTemplate("function('replace', {0}, {1}, {2})"
, member.username, "member", "M"))
.from(member)
.fetchFirst()
ansi 표준 함수들은 querydsl이 상당부분 내장하고 있어 아래와 같이 사용할 수 있다.
.where(member.username.eq(member.username.lower()))