ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 데이터 접근 기술- QueryDSL
    BackEnd/Spring DB 2023. 2. 4. 09:11
    반응형

    QueryDSL

    • Query + Domain + Specific + Language
    • 쿼리에 특화된 프로그래밍 언어
    • 단순, 간결, 유창
    • 다양한 저장소 쿼리 기능 통합

     

    JPA, MongoDB, SQL 같은 기술들을 위해 type-safe SQL을 만드는 프레임워크

     

    QueryDSL 장단점

    장점

    • 동적 쿼리 문제를 해결합니다.
    • 쿼리 문장에 오타가 있어도 컴파일 시점에 오류를 막을 수 있습니다.
    • 메서드 추출을 통해서 코드를 재사용할 수 있습니다.
    • 자바 코드로 쿼리를 작성할 수 있습니다.

     

    단점

    • Q코드 생성을 위한 APT를 설정해야 합니다.
    • 복잡한 쿼리의 경우 쿼리 분석이 어렵습니다. 전체 쿼리를 DBMS 툴로 조회하기 위해서는 실행되는 쿼리를 로깅하는 등의 방식으로 확인해야 합니다.

     

    QueryDSL 이전 정리 글

     

    QueryDSL 설정

    의존성 추가(build.gradle)

    dependencies {
    	//Querydsl 추가
    	implementation 'com.querydsl:querydsl-jpa'
    	annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jpa"
    	annotationProcessor "jakarta.annotation:jakarta.annotation-api"
    	annotationProcessor "jakarta.persistence:jakarta.persistence-api"
    }
    
    //Querydsl 추가, 자동 생성된 Q클래스 gradle clean으로 제거
    clean {
    	delete file('src/main/generated')
    }

     

    QueryDSL 적용

      QueryDSL을 사용하려면 JPAQueryFactory가 필요하며, JPAQueryFactory는 JPQL을 만들기 때문에 EntityManager가 필요합니다. 설정 방식은 JdbcTemplate과 유사합니다. JPAQueryFactory를 스프링 빈으로 등록해서 사용해도 됩니다.

     

      ItemRepository의 구현체와 Config 파일을 만들고 적용합니다.

    package hello.itemservice.repository.jpa;
    
    import com.querydsl.core.BooleanBuilder;
    import com.querydsl.core.types.Predicate;
    import com.querydsl.core.types.dsl.BooleanExpression;
    import com.querydsl.jpa.impl.JPAQueryFactory;
    import hello.itemservice.domain.Item;
    import hello.itemservice.domain.QItem;
    import hello.itemservice.repository.ItemRepository;
    import hello.itemservice.repository.ItemSearchCond;
    import hello.itemservice.repository.ItemUpdateDto;
    import org.springframework.stereotype.Repository;
    import org.springframework.transaction.annotation.Transactional;
    import org.springframework.util.StringUtils;
    
    import javax.persistence.EntityManager;
    import java.util.List;
    import java.util.Optional;
    
    import static hello.itemservice.domain.QItem.*;
    
    @Repository
    @Transactional
    public class JpaItemRepositoryV3 implements ItemRepository {
    
        private final EntityManager em;
        private final JPAQueryFactory query;
    
        public JpaItemRepositoryV3(EntityManager em) {
            this.em = em;
            this.query = new JPAQueryFactory(em);
        }
    
        @Override
        public Item save(Item item) {
            em.persist(item);
            return item;
        }
    
        @Override
        public void update(Long itemId, ItemUpdateDto updateParam) {
            Item findItem = em.find(Item.class, itemId);
            findItem.setItemName(updateParam.getItemName());
            findItem.setPrice(updateParam.getPrice());
            findItem.setQuantity(updateParam.getQuantity());
        }
    
        @Override
        public Optional<Item> findById(Long id) {
            Item item = em.find(Item.class, id);
            return Optional.ofNullable(item);
        }
    
        @Override
        public List<Item> findAll(ItemSearchCond cond) {
    
            String itemName = cond.getItemName();
            Integer maxPrice = cond.getMaxPrice();
    
            return query
                    .select(item)
                    .from(item)
                    .where(likeItemName(itemName), maxPrice(maxPrice))
                    .fetch();
        }
    
        private BooleanExpression likeItemName(String itemName) {
            if (StringUtils.hasText(itemName)) {
                return item.itemName.like("%" + itemName + "%");
            }
            return null;
        }
    
        private BooleanExpression maxPrice(Integer maxPrice) {
            if (maxPrice != null) {
                return item.price.loe(maxPrice);
            }
            return null;
        }
    }
    • save(), update(), findById(): 기본 기능들은 JPA가 제공하는 기본 기능을 사용합니다.
    • findAll(): QueryDSL을 사용해서 동적 쿼리 문제를 해결합니다. BooleanBuilder를 사용해서 원하는 where 조건들을 넣어주면 됩니다. where(A, B)에 다양한 조건들을 넣을 수 있는데, 이렇게 넣으면 AND 조건으로 처리됩니다. where()에 null인 경우, 해당 조건은 무시됩니다.
    package hello.itemservice.config;
    
    import hello.itemservice.repository.ItemRepository;
    import hello.itemservice.repository.jpa.JpaItemRepositoryV3;
    import hello.itemservice.service.ItemService;
    import hello.itemservice.service.ItemServiceV1;
    import lombok.RequiredArgsConstructor;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import javax.persistence.EntityManager;
    
    @Configuration
    @RequiredArgsConstructor
    public class QuerydslConfig {
    
        private final EntityManager em;
    
        @Bean
        public ItemService itemService() {
            return new ItemServiceV1(itemRepository());
        }
    
        @Bean
        public ItemRepository itemRepository() {
            return new JpaItemRepositoryV3(em);
        }
    
    }
    @Import(QuerydslConfig.class)
    @SpringBootApplication(scanBasePackages = "hello.itemservice.web")
    public class ItemServiceApplication {}

     

    QueryDSL 예외

      QueryDSL은 별도의 스프링 예외 추상화를 지원하지 않습니다. JPA처럼 @Repository에서 스프링 예외 추상화를 처리해줍니다.

     

    [참고 정보]

    스프링 DB 2편 - 데이터 접근 핵심 원리

    전체 소스코드

    반응형

    'BackEnd > Spring DB' 카테고리의 다른 글

    트랜잭션(Transaction) 심화  (0) 2023.02.11
    데이터 접근 기술- 활용  (0) 2023.02.09
    데이터 접근 기술- Spring Data JPA  (0) 2023.02.04
    데이터 접근 기술- JPA  (2) 2023.02.04
    데이터 접근 기술- MyBatis  (0) 2023.02.02

    댓글

Designed by Tistory.