QuerydslPredicateExecutor, QuerydslRepositorySupport 활용

WOOK JONG KIM·2022년 11월 1일
0
post-thumbnail
post-custom-banner

JPA에서는 QueryDSL을 더욱 편하게 사용할 수 있게 QuerydslPredicateExecutor 인터페이스와 QuerydslRepositorySupport 클래스를 제공

QuerydslPredicateExecutor 인터페이스

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

QueryDslPredicateExecutor를 상속받도록 설정한 Product 엔티티에 대한 리포지토리

public interface QProductRepository extends JpaRepository<Product, Long>,
        QuerydslPredicateExecutor<Product>{
    
}
public interface QuerydslPredicateExecutor<T> {
    Optional<T> findOne(Predicate predicate);

    Iterable<T> findAll(Predicate predicate);

    Iterable<T> findAll(Predicate predicate, Sort sort);

    Iterable<T> findAll(Predicate predicate, OrderSpecifier<?>... orders);

    Iterable<T> findAll(OrderSpecifier<?>... orders);

    Page<T> findAll(Predicate predicate, Pageable pageable);

    long count(Predicate predicate);

    boolean exists(Predicate predicate);

    <S extends T, R> R findBy(Predicate predicate, Function<FluentQuery.FetchableFluentQuery<S>, R> queryFunction);
}

대부분 Predicate를 인자로 받음
-> 표현식을 작성할 수 있게 QueryDSL에서 제공하는 인터페이스

test 디렉터리에 QProductRepository를 사용하기 위한 클래스 생성

findOne() 메서드 호출

	@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());
        }
    }

findAll() 메서드 호출

    @Test
    public void queryDSLTest2(){
        QProduct qProduct = QProduct.product;

        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());
        }
    }
}

QuerydslPredicateExecutor 사용 시 편하게 QueryDsl을 사용할 수 있지만 join이나 fetch와 같은 기능은 사용할 수 없다


QuerydslRepositorySupport 추상클래스 사용하기

보편적으로 CustomRepository를 활용해 리포지토리를 구현

지금까지 예로 든 Product 엔티티를 활용하기 위한 객체들의 상속 구조

여기서 JpaRepository와 QuerydslRepositorySupport는 JPA 에서 제공하는 인터페이스와 클래스

나머지 두 인터페이스와 하나의 클래스는 직접 구현 해야 함

  1. JpaRepository를 상속받는 ProductRepository 생성
  2. 직접 구현한 쿼리를 사용하기 위해 JpaRepository를 상속받지 않는 리포지토리 인터페이스인 ProductRepositoryCustom을 생성 -> 이 인터페이스에 정의하고자 하는 기능을 메서드로 정의
  3. ProductRepositoryCustom에서 정의한 메서드 사용하기 위해 ProductRepository에서 ProductRepository Custom을 상속 받음
  4. ProductRepositoryCustom 에서 정의한 메서드를 기반으로 실제 쿼리를 작성하기 위해 구현체인 ProductRepository CustomImpl 클래스를 생성
  5. ProductRepositoryCustomImpl 클래스에서는 다양한 방법으로 쿼리를 구현할 수 있지만 QueryDSL을 사용하기 위해 QueryDslRepositorySupport를 상속 받음

위와 같이 구성하면 DAO나 서비스에서 리포지토리에 접근하기 위해 ProductRepository 사용 -> 이를 통해 QueryDSL 기능도 사용 가능

ProductRepositoryCustom

public interface ProductRepositoryCustom {
    
    List<Product> findByName(String name);
}

인터페이스 생성하고 쿼리로 구현하고자 하는 메서드 정의

ProductRepositoryImpl

public class ProductRepositoryCustomImpl extends QuerydslRepositorySupport 
        implements ProductRepositoryCustom {
    
    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;
    }
}

QueryDSL을 사용하기 위해 QuerydslRepositorySupport를 상속받고 ProductRepositoryCustom 인터페이스 구현

이후 인터페이스에 정의된 메서드 구현
-> Q도메인 클래스인 QProduct를 사용해 QuerydslREpositorySupport가 제공하는 기능 사용

from()은 어떤 도메인에 접근할 지 지정 후 JPAQUERY 리턴

기존에 Product 엔티티 클래스와 매핑해서 사용하던 ProductRepository가 있다면 ProductRepository Custom을 상속받아 사용 가능

전 예제와 구분하기 위해 Repository 생성

@Repository("productRepositorySupport")
public interface ProductRepository extends JpaRepository<Product,Long> , ProductRepositoryCustom {
}

이전에 만든 리포지토리와의 충돌을 막기 위해 @REpository를 통해 별도의 이름 생성하였음

ProductRepository 이용 시 기본적으로 JpaRepository에서 제공하는 메서드도 사용할 수 있고, 별도로 ProductRepositoryCustom 인터페이스에서 정의한 메서드도 구현체를 통해 사용 가능

findbyName 메서드 호출해보기

public class 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
Journey for Backend Developer
post-custom-banner

0개의 댓글