JPA에서는 QueryDSL을 더욱 편하게 사용할 수 있게 QuerydslPredicateExecutor
인터페이스와 QuerydslRepositorySupport
클래스를 제공
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와 같은 기능은 사용할 수 없다
보편적으로 CustomRepository를 활용해 리포지토리를 구현
지금까지 예로 든 Product 엔티티를 활용하기 위한 객체들의 상속 구조
여기서 JpaRepository와 QuerydslRepositorySupport는 JPA 에서 제공하는 인터페이스와 클래스
나머지 두 인터페이스와 하나의 클래스는 직접 구현 해야 함
ProductRepositoryCustom
을 생성 -> 이 인터페이스에 정의하고자 하는 기능을 메서드로 정의ProductRepository CustomImpl
클래스를 생성위와 같이 구성하면 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());
}
}
}
리포지토리를 구성하면서 모든 로직 구현했기에 위와 같이 간단하게 구현해서 사용 가능