[Spring] 23.06.08 QueryDsl 동적쿼리 / 수정,삭제 벌크연산 / SQL Function 기능 (4)

hyewon jeong·2023년 6월 8일
0

Spring

목록 보기
58/65

1.동적 쿼리

  • 동적 쿼리를 해결하는 두가지 방식
  1. BooleanBuilder
  2. Where 다중 파라미터 사용

1-1.동적 쿼리 - BooleanBuilder 사용

@Test
public void 동적쿼리_BooleanBuilder() throws Exception {
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();
}

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

@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 조건에 null 값은 무시된다.
🎈메서드를 다른 쿼리에서도 재활용 할 수 있다.
쿼리 자체의 가독성이 높아진다.

조합 가능

  private BooleanExpression allEq(String usernameCond, Integer ageCond) {
      return usernameEq(usernameCond).and(ageEq(ageCond));
}

null 체크는 주의해서 처리해야함

PredicateBooleanExpression은 QueryDSL에서 사용되는 조건 표현을 나타내는 인터페이스입니다.

Predicate 보다는 BooleanExpression 을 사용하는 이유로는 BooleanExpression 은 and 와 or 같은 메소드들을 이용해서 BooleanExpression 을 조합해서 새로운 BooleanExpression 을 만들 수 있다는 장점이 있다. 그러므로 재사용성이 높다. 그리고 BooleanExpression 은 null 을 반환하게 되면 Where 절에서 조건이 무시되기 때문에 안전하다.

2.수정, 삭제 벌크 연산 -쿼리 한번으로 대량 데이터 수정

  long count = queryFactory
          .update(member)
.set(member.username, "비회원") 
.where(member.age.lt(28)) 
.execute();

기존 숫자에 1 더하기

long count = queryFactory
            .update(member)
            .set(member.age, member.age.add(1))
            .execute();

곱하기: multiply(x)

쿼리 한번으로 대량 데이터 삭제

    long count = queryFactory
            .delete(member)
            .where(member.age.gt(18))
            .execute();

주의: JPQL 배치와 마찬가지로, 영속성 컨텍스트에 있는 엔티티를 무시하고 데이터베이스에 직접 실행되기 때문에 배치 쿼리를 실행하고 나면 영속성컨텍스트와 데이터베이스가 동기화 되지 않는다. 따라서 영속성 컨텍스트를 초기화 하는 것이 안전하다.


@PersistenceContext
EntityManager em; 
    long count = queryFactory
            .delete(member)
            .where(member.age.gt(18))
            .execute();
            
   em.clear(); // 영속성 컨텍스트를 초기화하여 삭제된 회원들을 제거         

3.SQL function 호출하기

SQL function은 JPA와 같이 Dialect에 등록된 내용만 호출할 수 있다.
member M으로 변경하는 replace 함수 사용

Expressions.stringTemplate()

    String result = queryFactory
            .select(Expressions.stringTemplate("function('replace', {0}, {1},{2})", member.username, "member", "M"))
             .from(member)
          .fetchFirst();

소문자로 변경해서 비교해라.

  .select(member.username)
  .from(member)
  .where(member.username.eq(Expressions.stringTemplate("function('lower', {0})",member.username)))

lower 같은 ansi 표준 함수들은 querydsl이 상당부분 내장하고 있다. 따라서 다음과 같이 처리해도 결과 는 같다.

  .where(member.username.eq(member.username.lower()))

참고
김영한 QueryDsl 강의 자료

profile
개발자꿈나무

0개의 댓글