이번 스프링부트를 공부하며 상품정보를 보여주는 게시판의 검색기능을 구현중 기존 JPA만 가지고는 동적쿼리를 구현하는데 한계를 느끼고 방법을 찾는중 QueryDSL이라는 것을 찾았고
JPQL등 다른 동적쿼리 방식도 있었지만 QueryDSL이 제일 코드가 갈끔해서 사용하기로 했다.
Querydsl 정적 타입을 이용해서 SQL과 같은 쿼리를 생성할 수 있도록 해 주는 프레임워크다.
build.gradle 설정
dependencies { ... implementation 'com.querydsl:querydsl-jpa' implementation 'com.querydsl:querydsl-apt' annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jpa" annotationProcessor 'jakarta.persistence:jakarta.persistence-api' annotationProcessor 'jakarta.annotation:jakarta.annotation-api' } def querydslSrcDir = 'src/main/generated' sourceSets { main { java { srcDirs += [ querydslSrcDir ] } } } compileJava { options.compilerArgs << '-Aquerydsl.generatedAnnotationClass=javax.annotation.Generated' } tasks.withType(JavaCompile) { options.generatedSourceOutputDirectory = file(querydslSrcDir) } clean { delete file(querydslSrcDir) }
Config파일 설정
import com.querydsl.jpa.impl.JPAQueryFactory; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class QuerydslConfig { @PersistenceContext private EntityManager entityManager; @Bean public JPAQueryFactory jpaQueryFactory() { return new JPAQueryFactory(entityManager); } }
ProductService.java 구현
public List<ProductEntity> getProduct(SearchDto request, Integer memberNo) { List<ProductEntity> list = productRepository.findBySearch(memberNo, request); return list; }
ProductRepository.java 구현
public interface ProductRepository extends JpaRepository<ProductEntity, Integer>, ProductRepositoryCustom { }
ProductRepositoryCustom.java 구현
public interface ProductRepositoryCustom { public List<ProductEntity> findBySearch(Integer memberNo, SearchDto request); }
ProductRepositoryImpljava 구현
파일명은 Impl로 끝나야함import java.time.LocalDate; import java.util.List; import org.springframework.stereotype.Repository; import com.querydsl.core.types.dsl.BooleanExpression; import com.querydsl.jpa.impl.JPAQueryFactory; import static com.example.runi.member.domain.entity.QProductEntity.product; import com.example.runi.member.domain.dto.SearchDto; import com.example.runi.member.domain.entity.ProductEntity; @Repository public class ProductRepositoryImpl implements ProductRepositoryCustom { private final JPAQueryFactory queryFactory; public ProductRepositoryImpl(JPAQueryFactory jpaQueryFactory) { this.queryFactory = jpaQueryFactory; } public List<ProductEntity> findBySearch(Integer memberNo, SearchDto reqeust) { return queryFactory .selectFrom(product) .where( product.memberNo.eq(memberNo), select(reqeust), date(reqeust) ) .orderBy(product.no.desc()) .fetch(); } //조건검색 private BooleanExpression select(SearchDto reqeust) { String select = reqeust.getSelect(); String search = reqeust.getSearch(); if(search.isEmpty()) return null; try { if(select.equals("0")) { return product.no.eq(Integer.parseInt(search)); } else if(select.equals("1")) { return product.productName.eq(search); } else if(select.equals("2")) { return product.price.eq(Integer.parseInt(search)); } else if(select.equals("3")) { return product.saveDate.eq(LocalDate.parse(search)); } else { return null; } } catch(Exception e) { return null; } } //날짜 검색 private BooleanExpression date(SearchDto reqeust) { String startDate = reqeust.getStartDate(); String endDate = reqeust.getEndDate(); if(!startDate.isEmpty() && endDate.isEmpty()) { return product.saveDate.goe(LocalDate.parse(startDate)); }else if(startDate.isEmpty() && !endDate.isEmpty()) { return product.saveDate.loe(LocalDate.parse(endDate)); }else if(!startDate.isEmpty() || !endDate.isEmpty()) { return product.saveDate.between(LocalDate.parse(startDate), LocalDate.parse(endDate)); }else { return null; } } }