FireDrago
[QueryDSL] 중급 문법 본문
프로젝션(select 대상) 지정 결과 반환 - 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;
}
}
DTO 코드를 만들어준다. select 에서 DTO로 결과를 받아보자
Querydsl 빈 생성(Bean population)
1. 프로퍼티 접근 : Projections.bean(클래스, 파라미터)
@Test
void findDtoBySetter() {
List<MemberDto> result = queryFactory
// 프로퍼티 접근 setter 사용
.select(Projections.bean(MemberDto.class,
member.username,
member.age))
.from(member)
.fetch();
for (MemberDto memberDto : result) {
System.out.println("memberDto = " + memberDto);
}
}
2. 필드 직접 접근 : Projections.fields(클래스, 파라미터)
@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);
}
}
3. 생성자 사용 : Projections.constructor(클래스, 파라미터)
@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);
}
}
※ 서브쿼리를 사용하여 별칭이 다를때
@Test
void findUserDto() {
// QMember 별칭 생성
QMember memberSub = new QMember("memberSub");
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();
}
4. @QueryProjection
@Data
public class MemberTeamDto {
private Long memberId;
private String username;
private int age;
private Long teamId;
private String teamName;
@QueryProjection
public MemberTeamDto(Long memberId, String username,
int age, Long teamId, String teamName) {
this.memberId = memberId;
this.username = username;
this.age = age;
this.teamId = teamId;
this.teamName = teamName;
}
}
MemberTeamDto 에 @QueryProjection 을 생성자에 붙여준다.
gradle => others => compile java 클릭하여 QMember 객체를 생성한다.
@Test
void findDtoByQueryProjection() {
List<MemberDto> result = queryFactory
// 컴파일시 타입 체크할 수 있다.
// new QMemberDto() 생성한다.
.select(new QMemberDto(member.username, member.age))
.from(member)
.fetch();
for (MemberDto memberDto : result) {
System.out.println("memberDto = " + memberDto);
}
}
동적쿼리 - BooleanBuilder 사용
@Test
void 동적쿼리_BooleanBuilder() throws Exception {
String username = "member1";
Integer ageParam = 10;
List<Member> result = searchMember1(username, ageParam);
assertThat(result.size()).isEqualTo(1);
}
private List<Member> searchMember1(String userCond, Integer ageCond) {
BooleanBuilder builder = new BooleanBuilder();
if (userCond != null) {
builder.and(member.username.eq(userCond));
}
if (ageCond != null) {
builder.and(member.age.eq(ageCond));
}
return queryFactory
.selectFrom(member)
.where(builder)
.fetch();
}
동적쿼리 - Where 다중 파라미터 사용
@Test
void 동적쿼리_WhereParam() throws Exception {
String username = "member1";
Integer ageParam = 10;
List<Member> result = searchMember2(username, ageParam);
assertThat(result.size()).isEqualTo(1);
}
private List<Member> searchMember2(String userCond, Integer ageCond) {
return queryFactory
.selectFrom(member)
.where(allEq(userCond, ageCond))
.fetch();
}
private BooleanExpression usernameEq(String userCond) {
return userCond != null ? member.username.eq(userCond) : null;
}
private BooleanExpression ageEq(Integer ageCond) {
return ageCond != null ? member.age.eq(ageCond) : null;
}
// 메서드를 다른 쿼리에서도 재활용 할 수 있다. (장점)
private BooleanExpression allEq(String usernameCond, Integer ageCond) {
return usernameEq(usernameCond).and(ageEq(ageCond));
}
- where 조건에 null 값은 무시된다.
- 쿼리 자체의 가독성이 높아진다.
수정, 삭제 벌크연산
@Test
void bulkUpdate() {
queryFactory
.update(member)
.set(member.username, "비회원")
.where(member.age.lt(20))
.execute();
// 영속성 컨텍스트 초기화 반드시 해주자
em.flush();
em.clear();
}
@Test
void bulkDelete() {
queryFactory
.delete(member)
.where(member.age.gt(18))
.execute();
em.flush();
em.clear();
}
영속성 컨텍스트에 있는 엔티티를 무시하고 실행되기 때문에 배치 쿼리를 실행하고 나면
영속성 컨텍스트를 초기화 하는 것이 안전하다.
SQL function 호출하기
@Test
void sqlFunction() {
List<String> result = queryFactory
.select(Expressions.stringTemplate("function('replace', {0}, {1}, {2})",
member.username, "member", "M"))
.from(member)
.fetch();
for (String s : result) {
System.out.println("s = " + s);
}
}
@Test
void sqlFunction2() {
List<String> result = queryFactory
.select(member.username)
.from(member)
// .where(member.username.eq(Expressions.stringTemplate("function('lower', {0})",
// member.username)))
.where(member.username.eq(member.username.lower()))
.fetch();
for (String s : result) {
System.out.println("s = " + s);
}
}
lower 같은 ansi 표준 함수들은 querydsl 이 상당부분 내장하고 있다.
