해당 시리즈는 김영한님의 JPA 로드맵을 따라 학습하면서 내용을 정리하는 글입니다
프로젝션
: SELECT
대상 지정List<String> result = queryFactory
.select(member.username)
.from(member)
.fetch();
DTO
로 조회를 합니다DTO
를 사용하는 것을 지향합니다com.querydsl.core.Tuple
List<Tuple> result = queryFactory
.select(member.username, member.age)
.from(member)
.fetch();
for (Tuple tuple : result) {
String username = tuple.get(member.username);
Integer age = tuple.get(member.age);
System.out.println("username=" + username);
System.out.println("age=" + age);
JPA
에서 DTO
조회@Data
public class MemberDto {
private String username;
private int age;
public MemberDto() {}
public MemberDto(String username, int age) {
this.username = username;
this.age = age;
}
}
List<MemberDto> result = em.createQuery(
"SELECT new study.querydsl.dto.MemberDto(m.username, n.age) "
+ "from Member m", MemberDto.class)
.getResultList();
JPA
에서 DTO
로 조회할 때는 new
명령어를 사용해야 합니다DTO
의 package
이름을 다 적어줘야해서 지저분한 감이 있습니다DTO
로 반환할 때 사용(3가지 방법)List<MemberDto> result = queryFactory
.select(Projections.bean(MemberDto.class,
member.username,
member.age))
.from(member)
.fetch();
List<MemberDto> result = queryFactory
.select(Projections.fields(MemberDto.class,
member.username,
member.age))
.from(member)
.fetch();
@Data
public class UserDto {
private String name;
private int age;
}
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();
ExpressionUtils.as(source,alias)
: 필드
나, 서브 쿼리
에 별칭 적용username.as("memberName")
: 필드
에 별칭 적용List<MemberDto> result = queryFactory
.select(Projections.constructor(MemberDto.class
member.username,
member.age))
.from(member)
.fetch();
@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;
}
}
QMemberDto
가 생성됩니다List<MemberDto> result = queryFactory
.select(new QMemberDto(member.username, member.age))
.from(member)
.fetch();
DTO
에 QueryDSL
어노테이션을 유지해야 하는 점과 DTO
까지 Q클래스
파일을 생성해야 하는 단점이 있습니다BooleanBuilder
WHERE
다중 파라미터 사용@Test
public void 동적쿼리_BooleanBuilder() throws Exception {
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
public void 동적쿼리_WhereParam() throws Exception {
String usernameParam = "member1";
Integer ageParam = 10;
List<Member> result = searchMember2(usernameParam, ageParam);
assertThat(result.size()).isEqualTo(1);
private List<Member> searchMember2(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;
}
where
조건에 null
값은 무시됩니다long count = queryFactory
.update(member)
.set(member.username, "비회원")
.where(member.age.lt(28))
.execute();
long count = queryFactory
.update(member)
.set(member.age, member.age.add(1))
.execute();
long count = queryFactory
.update(member)
.set(member.age, member.age.multiply(2))
.execute();
long count = queryFactory
.delete(member)
.where(member.age.gt(18))
.execute();
JPQL
배치와 마찬가지로 영속성 컨텍스트에 있는 엔티티를 무시하고 실행되므로 배치 쿼리를 실행하고 나면 영속성 컨텍스트를 초기화하는 것이 안전합니다
SQL function
은 JPA
와 같이 Dialect
에 등록된 내용만 호출할 수 있습니다String result = queryFactory
.select(Expressions.stringTemplate(
"function('replace', {0}, {1}, {2})",
member.username, "member", "M"))
.from(member)
.fetchFirst();
.select(member.username)
.from(member)
.where(member.username.eq(
Expressions.stringTemplate("function('lower', {0})", member.username)))
-> lower
같은 ansi
표준 함수들은 QueryDSL
이 상당부분 내장하고 있습니다. 따라서 아래와 같이 처리해도 결과가 같습니다
.where(member.username.eq(member.username.lower()))