TIL

중급 문법

프로젝션과 결과 반환 - 기본

프로젝션 대상이 하나

List<String> result = queryFactory
 .select(member.username)
 .from(member)
 .fetch();

튜플 조회

@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 내부에서 쓰는 기술을 숨기자

프로젝션과 결과 반환 - DTO 조회

순수 JPA에서 DTO 조회

List<MemberDto> result = em.createQuery(
		 "select new study.querydsl.dto.MemberDto(m.username, m.age) " +
		 "from Member m", MemberDto.class)
 .getResultList();

Querydsl 빈 생성(Bean population)

프로퍼티 접근 방법 (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);
		}
}

필드 직접 접근

@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);
		}
}
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);
		}
}

프로젝션과 결과 반환 - @QueryProjection

생성자 + @QueryProjection

@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);
		}
}

동적 쿼리 - BooleanBuilder 사용

@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();
}

동적 쿼리 - Where 다중 파라미터 사용

@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;
}

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);
}