@Test
void projectionOne() {
List<String> result = queryFactory
.select(member.name)
.from(member)
.fetch();
assertThat(result).hasSize(4)
.contains("member1", "member2", "member3", "member4");
}
com.querydsl.core.Tuple
혹은 DTO로 조회@Test
void projectionTuple() {
List<Tuple> result = queryFactory
.select(member.name, member.age)
.from(member)
.fetch();
assertThat(result).hasSize(4);
result.forEach(tuple -> {
System.out.println("tuple.get(member.name) = " + tuple.get(member.name));
System.out.println("tuple.get(member.age) = " + tuple.get(member.age));
});
}
JPQL
사용@Test
void projectionDtoByJpql() {
// 단, 패키지명까지 상세히 적어야 한다.
List<MemberDto> result = em.createQuery(
"select new com.example.querydsl.controller.dto.MemberDto(m.name, m.age)"
+ " from Member m", MemberDto.class)
.getResultList();
assertThat(result).hasSize(4);
result.forEach(System.out::println);
}
@Test
void projectionDtoByQueryDsl() {
List<MemberDto> result = queryFactory
// .select(Projections.bean(MemberDto.class, member.name, member.age)) // 1. By Property (Setter)
// .select(Projections.fields(MemberDto.class, member.name, member.age)) // 2. By Field
.select(Projections.constructor(MemberDto.class, member.name, member.age)) // 3. By Constructor
.from(member)
.fetch();
assertThat(result).hasSize(4);
result.forEach(System.out::println);
}
Projections.bean(MemberDto.class, …)
as(alias)
를 통해 별칭 적용한다.ExpressionUtils.as(subQuery, alias)
Projections.fields(MemberDto.class, ...)
as(alias)
를 통해 별칭 적용한다.ExpressionUtils.as(subQuery, alias)
@Test
void projectionDtoWithDifferentFieldNames() {
QMember memberSub = new QMember("memberSub");
List<MemberDto> result = queryFactory
.select(Projections.constructor(
MemberDto.class,
member.name.as("name"),
ExpressionUtils.as(
JPAExpressions
.select(memberSub.age.max())
.from(memberSub), "age")
)
).from(member)
.fetch();
assertThat(result).hasSize(4);
result.forEach(memberDto ->
assertThat(memberDto.getAge()).isEqualTo(40)
);
}
Projections.constructor(MemberDto.class, ...)
@QueryProjection
을 사용@QueryProjectio
어노테이션 적용./gradlew compileQuerydsl
을 통해 Q-Type의 DTO 클래스 생성@Data
public class MemberDto {
private final String name;
private final Integer age;
@QueryProjection
public MemberDto(String name, Integer age) {
this.name = name;
this.age = age;
}
}
@Test
void projectionDtoWithQueryProjection() {
List<MemberDto> result = queryFactory
.select(new QMemberDto(member.name, member.age))
.from(member)
.fetch();
assertThat(result).hasSize(4);
result.forEach(System.out::println);
}
동적 쿼리를 해결하는 두가지 방식
BooleanBuilder
where(...)
다중 파라미터 사용com.querydsl.core.BooleanBuilder
private List<Member> searchMemberByBooleanBuilder(String name, Integer age) {
BooleanBuilder builder = new BooleanBuilder();
if (StringUtils.isNotBlank(name)) {
builder.and(member.name.eq(name));
}
if (Objects.nonNull(age)) {
builder.and(member.age.eq(age));
}
return queryFactory
.selectFrom(member)
.where(builder)
.fetch();
}
- `where(...)` 파라미터가 null이면 무시된다.
- 코드 예시
```java
private List<Member> searchMemberByWhere(String name, Integer age) {
return queryFactory
.selectFrom(member)
.where(
Optional.ofNullable(name).map(member.name::eq).orElse(null),
Optional.ofNullable(age).map(member.age::eq).orElse(null))
.fetch();
}
```
update(...)
, delete(…)
set(source, target)
execute()
JPQL 배치와 마찬가지로, 영속성 컨텍스트에 있는 엔티티를 무시하고 실행되기 때문에, 배치 쿼리를 실행한 후 영속성 컨텍스트를 초기화 하는 것이 안전하다.
@Test
void bulkUpdate() {
long batchCount = queryFactory
.update(member)
.set(member.name, member.name.concat("-YOUNG"))
.where(member.age.lt(28))
.execute();
assertThat(batchCount).isEqualTo(2);
List<Member> result = queryFactory
.selectFrom(member)
.fetch();
result.forEach(member -> {
if (member.getAge() < 28) {
assertThat(member.getName()).endsWith("-YOUNG");
}
});
}
@Test
void bulkUpdateByAddOrMultiply() {
long batchCount = queryFactory
.update(member)
.set(member.age, member.age.add(1))
// .set(member.age, member.age.multiply(2))
// .set(member.age, member.age.subtract(10))
// .set(member.age, member.age.divide(2))
.execute();
assertThat(batchCount).isEqualTo(4);
List<Member> result = queryFactory
.selectFrom(member)
.fetch();
assertThat(result).extracting(Member::getAge)
.containsOnly(11, 21, 31, 41);
}
@Test
void bulkDelete() {
long batchCount = queryFactory
.delete(member)
.where(member.age.gt(18))
.execute();
assertThat(batchCount).isEqualTo(3);
Long count = queryFactory.select(member.countDistinct()).from(member).fetchOne();
assertThat(count).isEqualTo(1);
}
Expressions.stringTemplate(sqlFunction, ...)
Expressions.stringTemplate("function('replace', {0}, {1}, {2})", member.name, "member", "M")
@Test
void sqlFunctionByExpressionsStringTemplate() {
List<String> result = queryFactory
.select(Expressions.stringTemplate("function('replace', {0}, {1}, {2})", member.name,
"member", "M"))
.from(member)
.fetch();
result.forEach(name -> assertThat(name).startsWith("M"));
}
@Test
void sqlFunctionByExpressionsStringTemplate2() {
List<String> result = queryFactory
.select(member.name)
.from(member)
.where(member.name.eq(Expressions.stringTemplate("function('lower', {0})", member.name)))
// .where(member.name.eq(member.name.lower()))
.fetch();
result.forEach(name -> assertThat(name).isLowerCase());
}