where의 인자를 여러개 주면 자동으로 and로 묶어지는 특성을 이용하여 동적 쿼리를 만들 수 있다.
private Predicate usernameEq(String usernameCond){
return usernameCond != null ? m.username.eq(usernameCond) : null;
}
private Predicate ageEq(Integer ageCond){
return ageCond != null? m.age.eq(ageCond) : null;
}
먼저 null이 아닐 경우 eq 표현식을 반환하는 메소드를 만들어 줬다.
그리고,
private List<Member> searchMember2(String usernameCond, Integer ageCond){
return query.selectFrom(m).where(usernameEq(usernameCond), ageEq(ageCond))
.fetch();
}
그냥 이렇게 쓰면 된다.
따지고 보자면 where안에
where( m.username.eq(usernameCond), m.age.eq(ageCond))
이렇게 들어간 것이다.
where같은 경우는 표현식이 인자로 여러개가 들어간다면 그걸 and로 묶도록 해놨다.
@Test
public void dynamicQuery_WhereParam(){
String usernameParam = "member1";
Integer ageParam = 10;
List<Member> result = searchMember2(usernameParam, ageParam);
assertThat(result.size()).isEqualTo(1);
}
돌려 보자면,
/* select
member1
from
Member member1
where
member1.username = ?1
and member1.age = ?2 */ select
m1_0.member_id,
m1_0.age,
m1_0.team_id,
m1_0.username
from
member m1_0
where
m1_0.username=?
and m1_0.age=?
이렇게 나간다.
의도한 대로 쿼리가 나간다.
@Test
public void dynamicQuery_WhereParam(){
String usernameParam = "member1";
Integer ageParam = null;
List<Member> result = searchMember2(usernameParam, ageParam);
assertThat(result.size()).isEqualTo(1);
}
조건이 하나만 있는 경우
/* select
member1
from
Member member1
where
member1.username = ?1 */ select
m1_0.member_id,
m1_0.age,
m1_0.team_id,
m1_0.username
from
member m1_0
where
m1_0.username=?
and로 묶지 않는다.
아예 where 인자의 predicate 자체가 null이라면 where문 조차 생성하지 않도록 짜여져 있다.
private Predicate usernameEq(String usernameCond){
return usernameCond != null ? m.username.eq(usernameCond) : null;
}
private Predicate ageEq(Integer ageCond){
return ageCond != null? m.age.eq(ageCond) : null;
}
이렇게 하면 장점이, 재사용 할 수 있다.
물론 근데 Predicate는 술어, 뭐랄까.. 그냥 표현식? 저 표현식 그 자체? 라고 봐야 해서,
그것보다 더 많은 기능을 제공하는 BooleanExpression으로 바꾸는 것이 좋다.
private BooleanExpression usernameEq(String usernameCond){
return usernameCond != null ? m.username.eq(usernameCond) : null;
}
private BooleanExpression ageEq(Integer ageCond){
return ageCond != null? m.age.eq(ageCond) : null;
}
이거는 내부를 보면,
public abstract class BooleanExpression extends LiteralExpression<Boolean> implements Predicate {
이렇게 Predicate를 상속받아 있다(정확히는 구현했다지만 편의상 상속받았다고 표현). 물론 Predicate는 표현식을 상속받았으며, 다른 상속받은 것도 이름 보면 알겠지만 표현식을 상속받았다.
이건 들어가 보면 많은 기능이 있다.
Boolean? 그러니까 null이면 무시해버리고 and, or 등으로 묶거나 not, 또 as로 별칭도 줄 수 있다.
기능 상 으로는 아까 봤던 BooleanBuilder와 거의 비슷하다. 말 그대로 저 BooleanExpression을 build 해주는 클래스니.
여튼 저걸로 바꾸면 이제,
private BooleanExpression allEq(String usernameCond, Integer ageCond){
return usernameEq(usernameCond).and(ageEq(ageCond));
}
이런 식으로 BooleanExpression의 기능을 적극 활용하여 묶어버리는 것이 가능하다.
그러면,
@Test
public void dynamicQuery_WhereParam(){
String usernameParam = "member1";
Integer ageParam = 10;
List<Member> result = searchMember3(usernameParam, ageParam);
assertThat(result.size()).isEqualTo(1);
}
private List<Member> searchMember3(String usernameCond, Integer ageCond){
return query.selectFrom(m).where(allEq(usernameCond, ageCond))
.fetch();
}
/* select
member1
from
Member member1
where
member1.username = ?1
and member1.age = ?2 */ select
m1_0.member_id,
m1_0.age,
m1_0.team_id,
m1_0.username
from
member m1_0
where
m1_0.username=?
and m1_0.age=?
이렇게 굉장히 직관적으로 코드를 짤 수 있다.
또 좋은 점이 아까 봤던 것 처럼 저렇게 조립을 할 수 있다.
이 조립할 수 있다는 점이 큰 장점이다.
null체크나 조건 체크같은 건 알아서 잘 하자.
'스프링데이터 + JPA > QueryDSL' 카테고리의 다른 글
26. SQL 함수 호출 (0) | 2023.12.02 |
---|---|
25. 수정, 삭제 벌크연산 (0) | 2023.12.02 |
23. 동적쿼리. BooleanBuilder 방식 (0) | 2023.12.01 |
22. @QueryProjection (0) | 2023.12.01 |
21. 프로젝션 Dto (0) | 2023.12.01 |