제육's 휘발성 코딩
article thumbnail
반응형

QueryDSL

실무에서는 조건에 따라서 실행되는 쿼리가 달라지는 동적 쿼리를 많이 사용한다.

이전 방법

public List<Order> findAllByString(OrderSearch orderSearch) {
        //language=JPAQL
        String jpql = "select o From Order o join o.member m";
        boolean isFirstCondition = true;

        //주문 상태 검색
        if (orderSearch.getOrderStatus() != null) {
            if (isFirstCondition) {
                jpql += " where";
                isFirstCondition = false;
            } else {
                jpql += " and";
            }
            jpql += " o.status = :status";
        }

        //회원 이름 검색
        if (StringUtils.hasText(orderSearch.getMemberName())) {
            if (isFirstCondition) {
                jpql += " where";
                isFirstCondition = false;
            } else {
                jpql += " and";
            }
            jpql += " m.name like :name";
        }

        TypedQuery<Order> query = em.createQuery(jpql, Order.class) .setMaxResults(1000); //최대 1000건
        if (orderSearch.getOrderStatus() != null) {
            query = query.setParameter("status", orderSearch.getOrderStatus());
        }
        if (StringUtils.hasText(orderSearch.getMemberName())) {
            query = query.setParameter("name", orderSearch.getMemberName());
        }
        return query.getResultList();
    }
  • 동적 쿼리를 위한 JPQL 사용 방식
public List<Order> findALlByCriteria(OrderSearch orderSearch) {
  CriteriaBuilder cb = em.getCriteriaBuilder();
  CriteriaQuery<Order> cq = cb.createQuery(Order.class);
  Root<Order> o = cq.from(Order.class);
  Join<Order, Member> m = o.join("member", JoinType.INNER);

  List<Predicate> criteria = new ArrayList<>();

  //주문 상태 검색
  if (orderSearch.getOrderStatus() != null) {
    Predicate status = cb.equal(o.get("status"),
                                orderSearch.getOrderStatus());
    criteria.add(status);
  }

  //회원 이름 검색
  if (StringUtils.hasText(orderSearch.getMemberName())) {
    Predicate name =
      cb.like(m.<String>get("name"), "%" +
              orderSearch.getMemberName() + "%");
    criteria.add(name);
  }

  cq.where(cb.and(criteria.toArray(new Predicate[criteria.size()])));
  TypedQuery<Order> query = em.createQuery(cq).setMaxResults(1000); //최대 1000건
  return query.getResultList();
}
  • 동적 쿼리를 위한 Criteria 사용 방식

QueryDSL

//querydsl 추가
buildscript {
    ext {
        queryDslVersion = "5.0.0"
    }
}

plugins {
    id 'org.springframework.boot' version '2.6.6'
    id 'io.spring.dependency-management' version '1.0.11.RELEASE'
    id 'java'
    // querydsl 추가
    id "com.ewerk.gradle.plugins.querydsl" version "1.0.10"
}

group = 'jpabook'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'com.fasterxml.jackson.datatype:jackson-datatype-hibernate5'
    implementation 'org.springframework.boot:spring-boot-devtools'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
    implementation 'org.springframework.boot:spring-boot-starter-validation'
    implementation 'com.github.gavlyukovskiy:p6spy-spring-boot-starter:1.8.0'
    implementation 'org.springframework.boot:spring-boot-starter-web'

  //querydsl 추가
    implementation "com.querydsl:querydsl-jpa:${queryDslVersion}"
    implementation "com.querydsl:querydsl-apt:${queryDslVersion}"

    compileOnly 'org.projectlombok:lombok'
    runtimeOnly 'com.h2database:h2'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'

}

tasks.named('test') {
    useJUnitPlatform()
}

//querydsl 추가 시작
def querydslDir = "$buildDir/generated/querydsl"

querydsl {
    jpa = true
    querydslSourcesDir = querydslDir
}

sourceSets {
    main.java.srcDir querydslDir
}

configurations {
    querydsl.extendsFrom compileClasspath
}

compileQuerydsl {
    options.annotationProcessorPath = configurations.querydsl
}
//querydsl 추가 끝
  • 스프링부트 2.6 부터는 querydsl 5.0.0 버전을 사용해야한다. (작성 후 Gradle - other - compileQuerydsl 더블클릭)

image

  • 다음과 같이 Q클래스들이 생성된 것을 볼 수 있다.
public List<Order> findAll(OrderSearch orderSearch) {
  QOrder order = QOrder.order;
  QMember member = QMember.member;

  JPAQueryFactory query = new JPAQueryFactory(em);
  return query.select(order)
    .from(order)
    .join(order.member, member)
    .where(statusEq(orderSearch.getOrderStatus()), nameLike(orderSearch.getMemberName()))
    .limit(1000)
    .fetch();
}

private BooleanExpression statusEq(OrderStatus statusCond) {
  if (statusCond == null) {
    return null;
  }
  return QOrder.order.status.eq(statusCond);
}

private BooleanExpression nameLike(String nameCond) {
  if (!StringUtils.hasText(nameCond)) {
    return null;
  }
  return QMember.member.name.like(nameCond);
}
  • 다음과 같이 SQL과 모양이 유사하면서 자바 코드로 동적 쿼리를 편리하게 작성할 수 있다.

 

Querydsl은 JPQL 코드를 만드는 빌더 역할 뿐이다. JPQL을 잘 이해하면 금방 익힐 수 있다.

본 포스팅은 인프런 김영한님 강의(실전! 스프링 부트와 JPA 활용2)를 토대로 정리한 내용입니다.

반응형
profile

제육's 휘발성 코딩

@sasca37

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요! 맞구독은 언제나 환영입니다^^