Spring/QueryDSL

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

느리지만 꾸준하게 2022. 4. 21. 02:28

굉장히 유용하니 잘 알아두도록 하자.

 

앞서 했던 BooleanBuilder와 같은 기능을 하지만 코드가 다르다.

 

 

아래 코드에서 where절에 null이 들어가면 어떻게 될까?

// QuerydslBasicTest

@Test
public void dynamicQuery_WhereParam() {
    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 Predicate usernameEq(String usernameCond) {
    if (usernameCond == null) {
        return null;
    }
    return member.username.eq(usernameCond);

}

private Predicate ageEq(Integer ageCond) {
    return null;
}

여기서 where에 null이 있는데 where null이 들어가게 되면 

@Test
    public void dynamicQuery_WhereParam() {
        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(null, ageEq(ageCond))
                .fetch();
    }

    private Predicate usernameEq(String usernameCond) {
        if (usernameCond == null) {
            return null;
        }
        return member.username.eq(usernameCond);

    }

    private Predicate ageEq(Integer ageCond) {
        return null;
    }
}

 

where에 null이 들어가게 되면 무시가 된다. where는 and 조건인데 응답값이 null이면 null이 무시가 되는 것이다.

private List<Member> searchMember2(String usernameCond, Integer ageCond) {
    return queryFactory
            .selectFrom(member)
            .where(null, ageEq(ageCond))
            .fetch();
}

 

삼항 연산자도 아래와 같이 줄 수 있다.

// 삼항연산자를 줄수가 있다.
// null이 아니면 ~조건 그게아니면 null
private Predicate usernameEq(String usernameCond) {
    return usernameCond != null ? member.username.eq(usernameCond) : null;
}

 

 

 

 

테스트를 돌리면 

 

@Test
public void dynamicQuery_WhereParam() {
    String usernameParam = "member1";
    Integer ageParam = 10;

    List<Member> result = searchMember2(usernameParam, ageParam);
    assertThat(result.size()).isEqualTo(1);
}

// member 조회하고 usernameEq하니까 ageEq보다.
private List<Member> searchMember2(String usernameCond, Integer ageCond) {
    return queryFactory
            .selectFrom(member)
            .where(usernameEq(usernameCond), ageEq(ageCond))
            .fetch();
}

// 삼항연산자를 줄수가 있다.
// null이 아니면 ~조건 그게아니면 null
private Predicate usernameEq(String usernameCond) {
    return usernameCond != null ? member.username.eq(usernameCond) : null;
}

private Predicate ageEq(Integer ageCond) {
    return ageCond != null ? member.age.eq(ageCond) : null;
}
// username와 age가 파라미터 바인딩 되어 넘어간다.
/* select
        member1 
    from
        Member member1 
    where
        member1.username = ?1 
        and member1.age = ?2 */ 
        
        select
            member0_.id as id1_1_,
            member0_.age as age2_1_,
            member0_.team_id as team_id4_1_,
            member0_.username as username3_1_ 
        from
            member member0_ 
        where
            member0_.username=? 
            and member0_.age=?

 

 

만약 ageParam이 null이 되면

where 절에서 null이 된다.

Integer ageParam = null;
.where(usernameEq(usernameCond), null)
@Test
public void dynamicQuery_WhereParam() {
    String usernameParam = "member1";
    Integer ageParam = null;

    List<Member> result = searchMember2(usernameParam, ageParam);
    assertThat(result.size()).isEqualTo(1);
}

// member 조회하고 usernameEq하니까 ageEq보다.
private List<Member> searchMember2(String usernameCond, Integer ageCond) {
    return queryFactory
            .selectFrom(member)
            .where(usernameEq(usernameCond), null)
            .fetch();
}

// 삼항연산자를 줄수가 있다.
// null이 아니면 ~조건 그게아니면 null
private Predicate usernameEq(String usernameCond) {
    return usernameCond != null ? member.username.eq(usernameCond) : null;
}

private Predicate ageEq(Integer ageCond) {
    return ageCond != null ? member.age.eq(ageCond) : null;
}

실행시키면 age가 없는 것을 확인할 수 있다.

/* select
        member1 
    from
        Member member1 
    where
        member1.username = ?1

 

 

 

자바 코드여서 컴포지션이 되는 방법인 조립을 할수가 있다.

아래와 같이 allEq를 만들어서 where절에 넣어주도록 해보자.

// member 조회하고 usernameEq하니까 ageEq구나하고 생각하면 된다.
    private List<Member> searchMember2(String usernameCond, Integer ageCond) {
        return queryFactory
                .selectFrom(member)
                .where(usernameEq(usernameCond), null)
//                .where(allEq(usernameCond, ageCond))
                .fetch();
    }
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));
}

 

 

그리고 여러가지 장점이 있는데 순서대로 파악하면사 읽어보자.

 

2

/ member 조회하고 usernameEq하니까 ageEq구나하고 생각하면 된다.
    private List<Member> searchMember2(String usernameCond, Integer ageCond) {
        return queryFactory
                .selectFrom(member)
//                .where(usernameEq(usernameCond), null)
                .where(isServicable())
//                .where(allEq(usernameCond, ageCond))
                .fetch();
    }

    // 다른 쿼리에서도 재활용이 된다.
    private List<Member> searchMember2(String usernameCond, Integer ageCond) {
        return queryFactory
                .selectFrom(new QMemberDto(member.username, member.age ))
                .from(member)
                .where(isServicable())
                .fetch();
    }

 

3

// 다른 쿼리에서도 재활용이 된다.
private List<Member> searchMember2(String usernameCond, Integer ageCond) {
    return queryFactory
            .selectFrom(new QMemberDto(member.username, member.age ))
            .from(member)
            .where(isServicable())
            .fetch();
}

 

1

// 광고 상태 isValid, 날짜가 IN: isServicable 조건들이 여러개 나올 수가 있다.
// 이런식으로 작성하면 컴포지션이 된다.

// 아래와 같이 작성이 가능하다.
private BooleanExpression isServicable(String usernameCond, Integer ageCond) {
    return isValid(usernameCond).and(DateBetweenIn(ageCond));
}

 

결론적으로

  • `where`조건에 `null`값은 무시가 되고
  • 메서드를 다른 쿼리에서도 재활용 할 수 있다.
  • 쿼리 자체의 가독성이 높아진다. 그리고 조합이 가능하다.
  • 편리하게 컴포지션 기능을 수행할 수 있다.
  • `null` 체크는 주의해서 처리해야한다.

 

 

 

 

<출처 김영한: 실전! Querydsl >

https://www.inflearn.com/course/Querydsl-%EC%8B%A4%EC%A0%84/dashboard

 

실전! Querydsl - 인프런 | 강의

Querydsl의 기초부터 실무 활용까지, 한번에 해결해보세요!, - 강의 소개 | 인프런...

www.inflearn.com