본문 바로가기
Spring/JPA

Querydsl 동적 쿼리 - 기초 예제

by YoonJong 2023. 2. 4.
728x90

Querydsl 학습을 하면서 기초이지만, 기초를 토대로 응용할 수 있기때문에 정리해보았습니다.

 

동적쿼리에 관련된 코드를 짜지 못해보았는데 연습하면서 접해볼 수 있어서 동적쿼리가 무엇인지, Querydsl를 왜 사용해야하는지 알게되었습니다.

 

해당 예제는 Dto 타입을 조회하는 것이 아닌 Entity 를 조회하는 예제입니다.

만들 예제는 아주아주 간단한, 설정한 값 사이에 있는 상품을 조회하는 것 입니다.

실제로,  상품이 000원~ 000원 사이의 상품을 검색해본 경험이 많아서 주제로 선택했습니다.

 

스프링 데이터 JPA 인터페이스를 따로 만들어주고, 사용자 정의 인터페이스를 하나 더 작성해주었습니다.

 

먼저, 스프링 데이터 JPA 인터페이스를 만들었습니다.

기본적인 CRUD 기능을 자동으로 지원해줍니다.

 

GoodsRepository 인터페이스

public interface GoodsRepository extends JpaRepository<Goods, Long> {
}

 

GoodsRepositoryCustom 인터페이스

Querydsl를 만들어 사용하기 위한 인터페이스 입니다. 인터페이스를 만들어주고 구현클래스를 만들어야 합니다.

간단하게 설정에 따른 검색을 할 수 있도록 만들었습니다.

public interface GoodsRepositoryCustom {

    List<Goods> search(GoodsSearchCondition condition);
}

 

GoodsRepositoryImpl 클래스

이제 Custom 인터페이스를 구현해야 합니다.

EntityManagerJPAQueryFactory를 생성자를 만들어줍니다.

 

search 를 구현한 쿼리에서 where 절의 betweenPrice 는 아래 BooleanExpression 타입으로 내부 메서드를 생성해 구현합니다.

원래 Predicate 타입으로 자동생성 되지만, BooleanExpression 타입으로 바꾼 이유는 and 또는 or 사용이 가능하기 때문입니다.

 

priceGoe 와 priceLoe 를 따로 만들었는데, 메서드를 다른 조건에서 재사용, 활용 할 수 있기 때문에 따로 작성했습니다.

+ 현재 betweenPrice 에 null 조건은 추가되지 않은 상태입니다.

 

@Repository
public class GoodsRepositoryImpl implements GoodsRepositoryCustom {

    private final EntityManager em;
    private final JPAQueryFactory queryFactory;

    public GoodsRepositoryImpl(EntityManager em) {
        this.em = em;
        this.queryFactory = new JPAQueryFactory(em);
    }

    @Override
    public List<Goods> search(GoodsSearchCondition condition) {
        return queryFactory
                .selectFrom(goods)
                .where(betweenPrice(condition.getPriceMin(), condition.getPriceMax()))
                .fetch();

    }

    // null 처리 필요
    private BooleanExpression betweenPrice(Integer priceMin, Integer priceMax) {
        return priceLoe(priceMax).and(priceGoe(priceMin));
    }

    private BooleanExpression priceGoe(Integer priceMax) {
        return priceMax != null ? goods.price.goe(priceMax) : null;
    }

    private BooleanExpression priceLoe(Integer priceMin) {
        return priceMin != null ? goods.price.loe(priceMin) : null;
    }
}

 

GoodsSearchCondition 클래스

상품을 어떤 조건으로 조회할지 필드값을 생성해줍니다.

저는 상품가격을 조회하려고 하기때문에 최소~ 최대 값을 조건으로 줄 계획입니다.

@Data
public class GoodsSearchCondition {

    private Integer priceMin;
    private Integer priceMax;

}

 

++ 내용추가

GoodsRepository 인터페이스에 GoodsRepositoryCustom 인터페이스를 상속시켜줍니다.

인터페이스이므로 여러개 상속을 받을 수 있습니다.

public interface GoodsRepository extends JpaRepository<Goods, Long>, GoodsRepositoryCustom {
}

 

 

이제 테스트 해봅니다.

25000~ 50000 원 사이의 상품을 조회해보겠습니다.

먼저, 상품을 추가하고 조건을 생성해주었습니다.

결과는 C상품과 D 상품이 조회되어야 성공입니다.

@SpringBootTest
@Transactional
class GoodsRepositoryImplTest {

    @Autowired
    GoodsRepository goodsRepository;

    @Test
    void betweenPrice() {
        Goods goodsA = new Goods("A상품", 10000);
        Goods goodsB = new Goods("B상품", 20000);
        Goods goodsC = new Goods("C상품", 30000);
        Goods goodsD = new Goods("D상품", 40000);

        goodsRepository.save(goodsA);
        goodsRepository.save(goodsB);
        goodsRepository.save(goodsC);
        goodsRepository.save(goodsD);

        GoodsSearchCondition condition = new GoodsSearchCondition();
        condition.setPriceMin(25000);
        condition.setPriceMax(50000);

        List<Goods> goodsList = goodsRepository.search(condition);
        for (Goods goods : goodsList) {
            System.out.println("goods = " + goods);
        }

        assertThat(goodsList)
                .extracting("name")
                .containsExactly("C상품","D상품");

    }

}

 

테스트가 정상적으로 성공했고, 조회도 되는 것을 볼 수 있습니다.

쿼리도 생각했던 내용이 출력되는 것을 볼 수 있습니다.

728x90

'Spring > JPA' 카테고리의 다른 글

@queryProjection 이란 ?  (0) 2023.02.24
querydsl 다중 조건 검색 만들기  (0) 2023.02.05
QueryDsl 설정 방법 - Spring boot 2.7.x  (1) 2023.02.03
List 타입을 Page 타입으로 리팩토링  (0) 2023.01.28
JPA N+1 문제 알아보기  (0) 2023.01.25

댓글