인프런 커뮤니티 질문&답변

갓다귀갓장국님의 프로필 이미지

작성한 질문수

실전! Querydsl

동적 쿼리 - Where 다중 파라미터 사용

강사님 where 다중 파라미터를 이용한 동적 쿼리 사용에 대한 질문입니다.

해결된 질문

작성

·

4.4K

28

강사님 강의 잘 보고 있습니다. 

다름아니라, where 다중 파라미터를 사용하면 가독성이 높아지는 건 이해했습니다.

영상 8분경의 메소드

    private BooleanExpression allEq(String userNameCond, Integer ageCond) {

        return userNameEq(userNameCond).and(ageEq(ageCond));

    }

에 대한 질문인데요 문제는 userNameCond가 null일 경우 userNameEq가 null을 반환하기 때문에 BooleanExpression으로 체이닝을 할 수가 없는데

혹시 이럴경우 null 걱정없이 강제로 체이닝 하는 방법은 없을까요?

BooleanExpression을 체이닝 하려고 해봤는데 추상클래스라 객체 생성이 안되네요

답변 9

21

김영한님의 프로필 이미지
김영한
지식공유자

갓다귀갓장국님 선물입니다. ㅋㅋㅋ 투척

private BooleanBuilder ageEq(Integer age) {
return nullSafeBuilder(() -> member.age.eq(age));
}

private BooleanBuilder roleEq(String roleName) {
return nullSafeBuilder(() -> member.roleName.eq(roleName));
}

public static BooleanBuilder nullSafeBuilder(Supplier<BooleanExpression> f) {
try {
return new BooleanBuilder(f.get());
} catch (IllegalArgumentException e) {
return new BooleanBuilder();
}
}

자바 8을 공부하시면 이렇게 코드를 더 줄일 수 도 있습니다.

좋은 하루 되세요^^

강사님 nullSafeBuilder 부분도 람다로 고쳐보고싶은데!..

 public static BooleanBuilder NullSafeBuilders(Supplier<BooleanExpression> o ){
       return Optional.ofNullable(o.get()).map(BooleanBuilder::new).orElseGet(BooleanBuilder::new);
    }

이렇게하면 익셉션이뜨더군요,,

 

그래서  이렇게도 해보았는데 똑같이 안되더군요 .. 람다로 어떻게 풀수있을까요?..

return Optional.ofNullable(o.get()).map(BooleanBuilder::new).orElseThrow(IllegalArgumentException::new);

김영한님의 프로필 이미지
김영한
지식공유자

이 코드를 람다로 풀 이유를 잘 모르겠습니다^^

혹시 아시는 분 있으면 답변 부탁드립니다.

8

김영한님의 프로필 이미지
김영한
지식공유자

안녕하세요. 갓다귀갓장국님 이제 끝이 보이는군요^^

다음 코드를 보시면 대략 어떻게 해야할지 감이 잡히실거에요.

@DataJpaTest
public class DynamicQueryTest {

JPAQueryFactory queryFactory;
@Autowired
EntityManager em;

@BeforeEach
void init() {
queryFactory = new JPAQueryFactory(em);

em.persist(new Member("userA", 10, "ROLE_MASTER"));
em.persist(new Member("userB", 20, "ROLE_ADMIN"));
em.persist(new Member("userC", 30, "ROLE_USER"));
}

@Test
void dynamicQuery() {

// Integer age = 10;
// String role = "ROLE_MASTER";
Integer age = null;
String role = null;

List<Member> result = queryFactory
.selectFrom(member)
.where(ageAndRoleEq(age, role))
.fetch();

System.out.println("result = " + result);

}

private BooleanBuilder ageAndRoleEq(Integer age, String role) {
return ageEq(age).and(roleEq(role));
}

private BooleanBuilder ageEq(Integer age) {
if (age == null) {
return new BooleanBuilder();
} else {
return new BooleanBuilder(member.age.eq(age));
}
}

private BooleanBuilder roleEq(String roleName) {
if (roleName == null) {
return new BooleanBuilder();
}
return new BooleanBuilder(member.roleName.eq(roleName));
}

}

감사합니다.

7

김영한님의 프로필 이미지
김영한
지식공유자

공부공부님 아쉽게도 ExpressionUtils.allOf를 사용해도 null처리는 따로 해주어야 합니다^^

다음 코드를 보시면 이해가 되실꺼에요.

return ExpressionUtils.allOf(member.age.eq(age), member.roleName.eq(roleName));

이렇게 풀어보면 member.age.eq(null) 이렇게 되는데, eq() 자체가 null을 받으면 예외가 발생합니다.

감사합니다.

5

김영한님의 프로필 이미지
김영한
지식공유자

안녕하세요. 참치캔님 하나씩 답변 달아드릴게요.

1. nullSafeBuilder 메서드는 다른 메서드처럼 private 인스턴스로 안 만드시고, public static메서드로 설계하셨는데 이 부분에 대해서 혹시 설명을 들어볼 수 있을까요??! 

-> 아~ 이것은 nullSafeBuilder를 공통으로 사용할 수 있는 유틸리티 클래스로 뽑아서 사용하라는 의미였습니다. 이 코드상에서는 private으로 하셔도 됩니다.

2. nullSafeBuilder 메서드에서catch로 NPE가 아닌 IllegalArgumentException 을 잡으신 이유에 대해서 궁금합니다!

Querydsl이 IllegalArgumentException을 호출합니다. 그래서 해당 예외를 잡았습니다.

public BooleanExpression eq(T right) {
if (right == null) {
throw new IllegalArgumentException("eq(null) is not allowed. Use isNull() instead");
} else {
return eq(ConstantImpl.create(right));
}

혹시 만약 nullSafeBuilder 메서드를 다른 클래스에서도 사용하려고 만드신거면 nullSafeBuilder 메서드는 어느 클래스나, 패키지에 귀속되는 게 맞을까요..??!!

-> 네 공통으로 적절하게 두시면 됩니다^^ 사실 계층을 명확하게 나눈다면 repository의 구현과 관련된 곳에 두는 것이 좋습니다.

감사합니다.

4

람다를 사용하니깐 더 깔끔해지네요. 생각지도 못한 선물 감사합니다  좋은 하루 보내세요^^

3

BooleanBuilder를 이렇게 활용하는 방식이 있었네요. 완전 응용이네요. 이방식 완전 좋네요 감사합니다^^

2

강사님 질문을 보다가 저도 궁금한게 생겨서 질문남겨봅니다..!

1. nullSafeBuilder 메서드는 다른 메서드처럼 private 인스턴스로 안 만드시고, public static메서드로 설계하셨는데 이 부분에 대해서 혹시 설명을 들어볼 수 있을까요??! 

2. nullSafeBuilder 메서드에서catch로 NPE가 아닌 IllegalArgumentException 을 잡으신 이유에 대해서 궁금합니다!

혹시 만약 nullSafeBuilder 메서드를 다른 클래스에서도 사용하려고 만드신거면 nullSafeBuilder 메서드는 어느 클래스나, 패키지에 귀속되는 게 맞을까요..??!!

감사합니다!

0

감사합니다. 아 저는 loe랑 goe로 실험했었는데 여기서는 NPE를 던졌는데 eq는 IllegalArgumentException 을 호출했네요..

0

아래처럼 하면 Predicate를 반환해도 되고, null 처리도 될거같네요.

import com.querydsl.core.types.ExpressionUtils;    

private Predicate ageAndRoleEq(Integer age, String role) {
return ExpressionUtils.allOf(ageEq(age), roleEq(role));
}