List<String> result = queryFactory
.select(member.username)
.from(member)
.fetch();
com.querydsl.core.Tuple
@Test
void tupleProjection() {
List<Tuple> tuples = queryFactory
.select(member.username, member.age)
.from(member)
.fetch();
for (Tuple tuple : tuples) {
String username = tuple.get(member.username);
Integer age = tuple.get(member.age);
System.out.println("username = " + username + " age = " + age);
}
}
Tuple은 querydsl 패키지에 포함된 객체다. Repository 계층 안에서 쓰는 건 괜찮지만 Service나 다른 영역에서까지 사용하면 좋은 설계가 아니다. Repository 내부에서 쓰는 기술을 숨기자
List<MemberDto> result = em.createQuery(
"select new study.querydsl.dto.MemberDto(m.username, m.age) " +
"from Member m", MemberDto.class)
.getResultList();
프로퍼티 접근 방법 (setter 사용)
@Test
void findDtoBySetter() {
List<MemberDto> result = queryFactory
.select(Projections.bean(MemberDto.class,
member.username,
member.age))
.from(member)
.fetch();
for (MemberDto memberDto : result) {
System.out.println("memberDto = " + memberDto);
}
}
Projections.bean()
사용setter()
와 기본 생성자 필요필드 직접 접근
@Test
void findDtoByField() {
List<MemberDto> result = queryFactory
.select(Projections.fields(MemberDto.class,
member.username,
member.age))
.from(member)
.fetch();
for (MemberDto memberDto : result) {
System.out.println("memberDto = " + memberDto);
}
}
Projections.fields()
사용setter()
없어도 됨ExpressionUtils.as(source, alias)
: 필드나, 서브 쿼리에 별칭 적용username.as("memberName")
: 필드에 별칭 적용List<UserDto> fetch = queryFactory
.select(Projections.fields(UserDto.class,
member.username.as("name"),
ExpressionUtils.as(
JPAExpressions
.select(memberSub.age.max())
.from(memberSub), "age")
)
).from(member)
.fetch();
생성자 사용
@Test
void findDtoByConstructor() {
List<MemberDto> result = queryFactory
.select(Projections.constructor(MemberDto.class,
member.username,
member.age))
.from(member)
.fetch();
for (MemberDto memberDto : result) {
System.out.println("memberDto = " + memberDto);
}
}
Projections.constructor()
사용생성자 + @QueryProjection
./gradlew compileQuerydsl
QMemberDto
생성 확인@Data
@NoArgsConstructor
public class MemberDto {
private String username;
private int age;
@QueryProjection
public MemberDto(String username, int age) {
this.username = username;
this.age = age;
}
}
@Test
void findDtoByQueryProjection() {
List<MemberDto> result = queryFactory
.select(new QMemberDto(member.username, member.age))
.from(member)
.fetch();
for (MemberDto memberDto : result) {
System.out.println("memberDto = " + memberDto);
}
}
@Test
void dynamicQuery_BooleanBuilder() {
String usernameParam = "member1";
Integer ageParam = 10;
List<Member> result = searchMember1(usernameParam, ageParam);
assertThat(result.size()).isEqualTo(1);
}
private List<Member> searchMember1(String usernameCond, Integer ageCond) {
BooleanBuilder builder = new BooleanBuilder();
if (usernameCond != null) {
builder.and(member.username.eq(usernameCond));
}
if (ageCond != null) {
builder.and(member.age.eq(ageCond));
}
return queryFactory
.selectFrom(member)
.where(builder)
.fetch();
}
@Test
void dynamicQuery_WhereParam() {
String usernameParam = "member1";
Integer ageParam = 10;
List<Member> result = queryFactory
.selectFrom(member)
.where(usernameEq(usernameParam), ageEq(ageParam))
.fetch();
assertThat(result.size()).isEqualTo(1);
}
private BooleanExpression usernameEq(String usernameCond) {
if (usernameCond != null) {
return member.username.eq(usernameCond);
}
return null;
}
private BooleanExpression ageEq(Integer ageCond) {
if (ageCond != null) {
return member.age.eq(ageCond);
}
return null;
}
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;
}
조합 가능
List<Member> result = queryFactory
.selectFrom(member)
.where(usernameEq(usernameParam).and(ageEq(ageParam)))
.fetch();
BooleanExpression
이 null이 반환될 경우 메서드 체이닝을 하면 예외가 발생할 것이다. 따라서 다음과 같이 BooleanBuilder
를 반환하는 방법이 있다. private BooleanBuilder usernameEq(String username) {
return nullSafeBuilder(() -> member.username.eq(username));
}
private BooleanBuilder ageEq(Integer age) {
return nullSafeBuilder(() -> member.age.eq(age));
}
public static BooleanBuilder nullSafeBuilder(Supplier<BooleanExpression> expressionSupplier) {
try {
return new BooleanBuilder(expressionSupplier.get());
} catch (IllegalArgumentException e) {
return new BooleanBuilder();
}
}
public class DynamicQuery {
private final BooleanBuilder builder;
private DynamicQuery() {
this.builder = new BooleanBuilder();
}
public static DynamicQuery builder() {
return new DynamicQuery();
}
public DynamicQuery and(Supplier<BooleanExpression> expressionSupplier) {
try {
this.builder.and(expressionSupplier.get());
} catch (IllegalArgumentException ignored) {}
return this;
}
public BooleanBuilder build() {
return this.builder;
}
}
@Test
void dynamicQuery_CustomBooleanBuilder() {
String usernameParam = "member1";
Integer ageParam = null;
List<Member> result = queryFactory
.selectFrom(member)
.where(DynamicQuery.builder()
.and(() -> member.username.eq(usernameParam))
.and(() -> member.age.eq(ageParam))
.build())
.fetch();
assertThat(result.size()).isEqualTo(1);
}