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 인터페이스를 구현해야 합니다.
EntityManager 와 JPAQueryFactory를 생성자를 만들어줍니다.
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상품");
}
}
테스트가 정상적으로 성공했고, 조회도 되는 것을 볼 수 있습니다.
쿼리도 생각했던 내용이 출력되는 것을 볼 수 있습니다.
'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 |
댓글