2 분 소요


Querydsl 설정

build.gradle

...
//Querydsl 추가
implementation 'com.querydsl:querydsl-jpa'
annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jpa"
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
annotationProcessor "jakarta.persistence:jakarta.persistence-api"

...

//Querydsl 추가, 자동 생성된 Q클래스 gradle clean으로 제거
clean {
    delete file('src/main/generated')
}

Q타입 생성

Gradle - Q타입 생성

  • Gradle -> Tasks -> build -> clean
  • Gradle -> Tasks -> other -> compileJava

Q타입 생성 확인

  • build -> generated -> sources -> annotationProcessor -> java/main 하위에 Q 타입이 생성되어 있어야 한다.

[참고]
Q타입은 컴파일 시점에 자동 생성되므로 버전관리(GIT)에 포함되지 않는 것이 좋다.
gradle 옵션을 선택하면 Q타입은 gradle build폴더 아래에 생성되기 때문에 여기를 포함하지 않아야 한다. 대부분 gradle build폴더를 git에 포함하지 않기 때문에 이 부분은 자연스럽게 해결된다.

IntelliJ IDEA - Q타입 생성

Build -> Build Project 또는
Build -> Rebuild 또는
main(), 또는 테스트를 실행하면 된다.

src/main/generated하위에 Q타입이 생성되어야 한다.

[참고]
IntelliJ IDEA 옵션을 선택하면 Q타입은 src/main/generated폴더 아래에 생성되기 때문에 버전관리에 이곳은 포함하지 않는 것이 좋다.


Querydsl 적용

JpaItemRepositoryV3

@Repository
@Transactional
public class JpaItemRepositoryV3 implements ItemRepository {

    private final EntityManager em;
    private final JPAQueryFactory query;

    public JpaItemRepositoryV3(EntityManager em) {
        this.em = em;
        this.query = new JPAQueryFactory(em);
    }

    @Override
    public Item save(Item item) {
        em.persist(item);
        return item;
    }

    @Override
    public void update(Long itemId, ItemUpdateDto updateParam) {
        Item findItem = findById(itemId).orElseThrow();
        findItem.setItemName(updateParam.getItemName());
        findItem.setPrice(updateParam.getPrice());
        findItem.setQuantity(updateParam.getQuantity());
    }

    @Override
    public Optional<Item> findById(Long id) {
        Item item = em.find(Item.class, id);
        return Optional.ofNullable(item);
    }

    public List<Item> findAllOld(ItemSearchCond itemSearch) {

        String itemName = itemSearch.getItemName();
        Integer maxPrice = itemSearch.getMaxPrice();
        
        QItem item = QItem.item;
        BooleanBuilder builder = new BooleanBuilder();

        if (StringUtils.hasText(itemName)) {
            builder.and(item.itemName.like("%" + itemName + "%"));
        }

        if (maxPrice != null) {
            builder.and(item.price.loe(maxPrice));
        }

        List<Item> result = query
                .select(item)
                .from(item)
                .where(builder)
                .fetch();

        return result;
    }

    @Override
    public List<Item> findAll(ItemSearchCond cond) {

        String itemName = cond.getItemName();
        Integer maxPrice = cond.getMaxPrice();

        List<Item> result = query
                .select(item)
                .from(item)
                .where(likeItemName(itemName), maxPrice(maxPrice))
                .fetch();

        return result;
    }

    private BooleanExpression likeItemName(String itemName) {

        if (StringUtils.hasText(itemName)) {
            return item.itemName.like("%" + itemName + "%");
        }
        return null;
    }
    private BooleanExpression maxPrice(Integer maxPrice) {

        if (maxPrice != null) {
            return item.price.loe(maxPrice);
        }
        return null;
    }
}

공통

  • Querydsl을 사용하려면 JPAQueryFactory가 필요하다. JPAQueryFactory는 JPA 쿼리인 JPQL을 만들기 때문에 EntityManager가 필요하다.
  • 설정 방식은 JdbcTemplate을 설정하는 것과 유사하다.
  • 참고로 JPAQueryFactory를 스프링 빈으로 등록해서 사용해도 된다.

findAllOld

Querydsl을 사용해서 동적 쿼리 문제를 해결한다.
BooleanBuilder를 사용해서 원하는 where조건을 넣어주면 된다.

findAll

앞서 findAllOld에서 작성한 코드를 리팩토링 했다.

List<Item> result = query
        .select(item)
        .from(item)
        .where(likeItemName(itemName), maxPrice(maxPrice))
        .fetch();
  • Querydsl에서 where(A,B)에 다양한 조건들을 직접 넣을 수 있는데, 이렇게 넣으면 AND 조건으로 처리된다. 참고로 where()null을 입력하면 해당 조건은 무시한다.
  • 이 코드의 또 다른 장점은 likeItemName(), maxPrice()를 다른 쿼리에서 재사용 할 수 있다는 점이다.(쿼리 조건을 부분적으로 모듈화)


정리

Querydsl 장점

  • Querydsl 덕분에 동적 쿼리를 매우 깔끔하게 사용할 수 있다.
  • 쿼리 문장에 오타가 있어도 컴파일 시점에 오류를 막을 수 있다.
  • 메서드 추출을 통해서 코드를 재사용할 수 있다.


<출처 : 인프런 - 스프링 DB 2편 : 데이터 접근 활용 기술(김영한)>

댓글남기기