Spring Boot (12) QueryDSL 실습

넙데데맨·2022년 8월 18일
0
post-custom-banner

Q도메인 클래스 활용 코드

  • JPAQuery QueryDSL을 사용하기 위한 객체로 엔티티 매니저를 통해 생성
  • 빌더 형식으로 쿼리 작성
  • fetch() 메소드를 통해 List 타입으로 값을 리턴 받음

JPAQuery 객체 활용

	@PersistenceContext
    EntityManager entityManager;

    @Test
    void queryDslTest(){
        JPAQuery<Product> query = new JPAQuery(entityManager);
        QProduct qProduct = QProduct.product;

        List<Product> productList = query
                .from(qProduct)
                .where(qProduct.name.eq("펜"))
                .orderBy(qProduct.price.asc())
                .fetch();

        for(Product product : productList){
            System.out.println("-----------------------");
            System.out.println();
            System.out.println("Product Number : " + product.getNumber());
            System.out.println("Product Name : " + product.getName());
            System.out.println("Product Price : " + product.getPrice());
            System.out.println("Product Stock : " + product.getStock());
            System.out.println("-----------------------");
        }
    }

JPAQueryFactory 활용

다음과 같이 select 절 부터 작성이 가능하다.

void queryDslTest2(){
        JPAQueryFactory jpaQueryFactory = new JPAQueryFactory(entityManager);
        QProduct qProduct = QProduct.product;

        List<String> productList = jpaQueryFactory.select(qProduct.name)
                .from(qProduct)
                .where(qProduct.name.eq("펜"))
                .orderBy(qProduct.price.asc())
                .fetch();

        for(String product : productList){
            System.out.println("-----------------------");
            System.out.println();
            System.out.println("Product Name : " + product);
            System.out.println("-----------------------");
        }
    }
     	/** 2개 이상을 조회할 시에는 Tuple 타입을 통해 리스트에 넣어준다. */
        List<Tuple> tupleList = jpaQueryFactory
                .select(qProduct.name, qProduct.price)
                .from(qProduct)
                .where(qProduct.name.eq("펜"))
                .orderBy(qProduct.price.asc())
                .fetch();

        for(Tuple tuple : tupleList){
            System.out.println("-----------------------");
            System.out.println("Product name : " + tuple.get(qProduct.name));
            System.out.println("Product name : " + tuple.get(qProduct.price));
            System.out.println("-----------------------");
        }

실제 비즈니스 로직에서 활용할 수 있게 설정

@Configuration
public class QueryDSLConfiguration {
    /** 엔티티 매니저 주입을 위한 어노테이션 */
    @PersistenceContext
    EntityManager entityManager;

    @Bean
    public JPAQueryFactory jpaQueryFactory(){
        return new JPAQueryFactory(entityManager);
    }
}

JPAQueryFactory 객체를 Bean으로 등록하면 초기화하지 않고 스프링 컨테이너에서 가져다 쓸 수 있다.

QueryDSL 편의 인터페이스, 클래스

QuerydslPredicateExecutor, QuerydslRepositorySupport

QuerydslPredicateExecutor

JpaRepository와 함께 리포지토리에서 QueryDSL을 사용할 수 있게 인터페이스 제공

public interface QProductRepository extends JpaRepository<Product, Long>, QuerydslPredicateExecutor<Product> {
}

queryDSLTEST1

@SpringBootTest
public class QProductRepositoryTest {
    @Autowired
    QProductRepository qProductRepository;

    @Test
    public void queryDSLTEST1(){
        Predicate predicate = QProduct.product.name.containsIgnoreCase("펜").and(QProduct.product.price.between(1000,2500));

        Optional<Product> foundProduct  = qProductRepository.findOne(predicate);

        if(foundProduct.isPresent()){
            Product product = foundProduct.get();
            System.out.println(product.getNumber());
            System.out.println(product.getName());
            System.out.println(product.getPrice());
            System.out.println(product.getStock());
        }
    }
}

queryDSLTEST2()

@Test
    public void queryDSLTEST2(){
        QProduct qProduct = QProduct.product;
        /** predicate 객체 선언하지 않고 그대로 서술부만 가져다가 사용*/
        Iterable<Product> productList = qProductRepository.findAll(
                qProduct.name.contains("펜")
                        .and(qProduct.price.between(550,1500))
        );

        for(Product product : productList){
            System.out.println(product.getNumber());
            System.out.println(product.getName());
            System.out.println(product.getPrice());
            System.out.println(product.getStock());
        }


    }

Predicate 간단하게 표현식으로 정의한 쿼리

contains 해당 이름 포함 시
containsIgnoreCase 해당 이름 포함 시(대소문자 구분 X)
and 조건 추가
Optional<T> findOne(Predicate) 1개 찾기
Iterable<T> findAll(Predicate) 여러 개 찾기(List, Page 등등)

QueryDSL을 간편하게 사용할 수 있게 하지만 Join, Fetch를 사용할 수 없다.

QuerydslRepositorySupport

CustomRepository를 활용해 리포지토리를 구현하는 방식으로 QueryDSL을 사용할 수 있게한다.

ProductRepository : JpaRepository, ProductRepositoryCustom 상속받음
ProductRepositoryCustom : QueryDslRepositorySupport 상속받음
QueryDslRepositorySupport : QueryDSL을 사용하기 위한 인터페이스

ProductRepositoryCustom

public interface ProductRepositoryCustom {
    /** 구현하고자하는 메서드 정의 */
    List<Product> findByName(String name);
}

ProductRepositoryCustomImpl

public class ProductRepositoryCustomImpl extends QuerydslRepositorySupport implements ProductRepositoryCustom {
    /** QuerydslRepositorySupport, ProductRepositoryCustom 상속 받아 QueryDSL 사용과 구현할 메소드 받아옴*/
    public ProductRepositoryCustomImpl(){
        super(Product.class);
    }

    @Override
    public List<Product> findByName(String name) {
        QProduct product = QProduct.product;

        List<Product> productList = from(product)
                .where(product.name.eq(name))
                .select(product)
                .fetch();
        return productList;
    }
}

ProductRepository

@Repository
public interface ProductRepository extends JpaRepository<Product,Long>, ProductRepositoryCustom {
	
}

ProductRepositoryTest

	@Autowired
    ProductRepository productRepository;

    @Test
    void findByNameTest(){
        List<Product> productList = productRepository.findByName("펜");

        for(Product product : productList){
            System.out.println(product.getNumber());
            System.out.println(product.getName());
            System.out.println(product.getPrice());
            System.out.println(product.getStock());
        }
    }
profile
차근차근
post-custom-banner

0개의 댓글