동적쿼리 사용 2가지 방법
동적 쿼리 사용을 위해서는 2가지 방법이 있습니다.
- BooleanBuilder
 - Where 다중 파라미터 사용
 
BooleanBuilder
where절에 BooleanBuilder를 넣는 방식입니다. and 혹은 or을 사용할 수 있습니다. 아래는 username과 age를 동적 쿼리로 조회합니다.
where 절의 핵심 코드는 아래와 같습니다.
...
.where(builder)
...
@Test
public void BooleanBuilderQuery() {
	String usernameParam = "member1";
	Integer ageParam = 10;
	List<Member> result = searchMember1(usernameParam, ageParam);
	Assertions.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절에는 username과 age 모두 검색조건에 들어가 있습니다.

age를 null로 바꾸고 쿼리문을 짜봅니다.
Integer ageParam = null;
위의 where 조건에는 age가 포함되어 있었으나, 이제 age가 포함되지 않습니다. 동적으로 null이므로 빠졌습니다.

참고로 방어코드를 짠다는 전제하에, BooleanBuilder에 초기값을 넣을 수도 있습니다.
BooleanBuilder builder = new BooleanBuilder(member.username.eq(usernameCond));
Where 다중 파라미터 사용
운영에서는 첫번째 BooleanBuilder보다는 Where 다중 파라미터 사용이 굉장히 편리합니다. where에서 좀 더 직관적으로 코드를 짤 수 있고 재사용도 가능합니다.
BooleanExpression을 리턴하는 usernameEq(String usernameCond), ageEq(Integer ageCond) 메서드를 만들어서 이름과나이를 검사할 수 있습니다.
where 절의 핵심 코드는 아래와 같습니다.
...
.where(usernameEq(usernameCond), ageEq(ageCond))
...
전체 코드는 아래와 같습니다.
@Test
public void 동적쿼리_WhereParam() throws Exception {
	String usernameParam = "member1";
	Integer ageParam = 10;
	List<Member> result = searchMember2(usernameParam, ageParam);
	Assertions.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 다중 파라미터 방식은 첫번째 방식 BooleanBuilder와 같은 JPQL 쿼리문이 동작합니다.

만약, usernameEq와 ageEq를 한번에 사용하고 싶다면 다음처럼 allEq() 함수를 만들어서 합칠 수 있습니다. BooleanExpression을 and로 연결하여 반환합니다.
@Test
public void 동적쿼리_WhereParam() throws Exception {
	String usernameParam = "member1";
	Integer ageParam = 10;
	List<Member> result = searchMember2(usernameParam, ageParam);
	Assertions.assertThat(result.size()).isEqualTo(1);
}
private List<Member> searchMember2(String usernameCond, Integer ageCond) {
	return queryFactory
			.selectFrom(member)
			//.where(usernameEq(usernameCond), ageEq(ageCond))
            .where(allEq(usernameCond, 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;
}
private BooleanExpression allEq(String usernameCond, Integer ageCond) {
	return usernameEq(usernameCond).and(ageEq(ageCond)); 
}
BooleanExpression이 composite할 수 있는 특징을 이용해서 여러상황을 만들 수 있고, 재사용이 가능합니다. 예를들어 광고 아이디와 날짜를 이용해 광고 가능한 상태인가를 판단하는 문법을 다음과 같이 만들 수 있습니다.
//핵심코드
...
where(isServiceable(id, date))
...
private BooleanExpressions isServiceable(Integer id, String date) {
	return isValid(id).and(DateBetweenIn(date);
}
동적쿼리를 검사할 때, 간단할 때 3항연산자를 쓰지만, if문으로 만들 수도 있습니다.
//삼항 연산자
private BooleanExpression usernameEq(String usernameCond) {
	return usernameCond != null ? member.username.eq(usernameCond) : null;
}
//if,else
private BooleanExpression usernameEq(String usernameCond) {
	if(usernameCond == null) return null;
    
	return member.username.eq(usernameCond);
}
1,2번 방식을 비교해보겠습니다.
//1. BooleanBuilder
.where(builder);
//2. where 다중 파라미터 사용
.where(usernameEq(usernameCond), ageEq(ageCond));
BooleanBuilder 보다는 확실히 Where 다중 파라미터 사용이 where문을 봤을 때 좀 더 직관적으로 이해 할 수 있습니다. 또한 where 다중 파라미터 사용을 한다면 메서드들을 재활용할 수 있습니다.
물론, 동적쿼리인만큼 항상 null 체크에 주의해야 합니다.
* 출처
Inflearn : 실전! Querydsl 강의
'Spring > Querydsl' 카테고리의 다른 글
| 순수 JPA와 Querydsl (0) | 2022.02.13 | 
|---|---|
| QueryDsl 수정, 삭제 (0) | 2022.02.13 | 
| QueryDsl 프로젝션 문법 (0) | 2022.02.13 | 
| QueryDsl 기본문법 (0) | 2022.02.12 | 
| JPA & JPQL & QueryDsl (0) | 2022.02.12 |