스프링 부트와 JPA 활용1 - 주문 도메인 개발 4

JOY·2022년 4월 23일
0
post-thumbnail

📌 스프링 부트와 JPA 활용1 - 주문 도메인 개발 4

인프런 - 스프링 부트와 JPA 활용1 by 김영한 을 기반으로 작성된 글입니다.
실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발


주문 도메인 개발 목차

1. 주문, 주문상품 엔티티 개발

2. 주문 리포지토리 개발

3. 주문 서비스 개발

4. 주문 기능 테스트

5. 주문 검색 기능 개발


검색 조건 파라미터

package jpabook.jpashop.domain;

@Getter @Setter
public class OrderSearch {

    private String memberName; //회원 이름
    private OrderStatus orderStatus; //주문상태[ORDER, CANCEL]
    
}

주문 검색 기능 개발 (OrderRepository.java)

JPA에서 동적 쿼리 해결하기

public List<Order> findAll(OrderSearch orderSearch) {

        return em.createQuery("select o from Order o join o.member m" +
                        "where o.status = :status " +
                        " and m.name like :name", Order.class)
                .setParameter("status", orderSearch.getOrderStatus())
                .setParameter("name", orderSearch.getMemberName())
                .setMaxResults(1000) //최대 1000건
                .getResultList();
    }

📍 설명

  • 검색 조건에 동적 쿼리를 생성해 주문 엔티티를 조회한다.

만약 ❓ Ordersearch에 name, status 파라미터가 없다면
name 과 status 를 필터링 조건으로 사용하지 말고 주문 내역을 모두 가지고 와라

라고 한다면 아래 코드 처럼 되어야 한다

    public List<Order> findAll(OrderSearch orderSearch) {

        return em.createQuery("select o from Order o join o.member m",
                .setMaxResults(1000) //최대 1000건
                .getResultList();
    }

이것을 동적 쿼리로 처리해주어야 한다

방법 1) JPQL 로 처리

💻 코드

//주문 상태 검색
public List<Order> findAll(OrderSearch orderSearch) {

        String jpql = "select o From Order o join o.member m";
        boolean isFirstCondition = true;

        //주문 상태 검색
        if (orderSearch.getOrderStatus() != null) {
            if (isFirstCondition) {
                jpql += " where";
                isFirstCondition = false;
            } else {
                jpql += " and";
            }
            jpql += " o.status = :status";
        }

        //회원 이름 검색
        if (StringUtils.hasText(orderSearch.getMemberName())){
            if(isFirstCondition){
                jpql += " where";
                isFirstCondition = false;
            }else{
                jpql += " and";
            }
            jpql += "m.name like :name";
        }

        TypedQuery<Order> query = em.createQuery(jpql, Order.class)
                                 .setMaxResults(1000);

        if (orderSearch.getOrderStatus() != null) {
            query = query.setParameter("status", orderSearch.getOrderStatus());
        }
        if (StringUtils.hasText(orderSearch.getMemberName())) {
            query = query.setParameter("name", orderSearch.getMemberName());
        }
        return query.getResultList();
    }

📍 설명

JPQL 쿼리를 문자로 생성하기가 번거롭고 실수로 인한 버그가 충분히 발생할 수 있다

❗ MyBatis, iBatis를 사용하면 동적쿼리를 생성하기 편리하다

방법 2) JPA Criteria 로 처리

💻 코드

	/**
     * JPA Criteria
     */
    public List<Order> findAllByCriteria(OrderSearch orderSearch){
        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<Order> cq = cb.createQuery(Order.class);
        Root<Order> o = cq.from(Order.class);
        Join<Object, Object> m = o.join("member", JoinType.INNER);

        List<Predicate> criteria = new ArrayList<>();

        //주문 상태 검색
        if (orderSearch.getOrderStatus() != null){
            Predicate status = cb.equal(o.get("status"), orderSearch.getOrderStatus());
            criteria.add(status);
        }

        //회원 이름 검색
        if (StringUtils.hasText(orderSearch.getMemberName())){
            Predicate name =
                    cb.like(m.<String>get("name"), "%" + orderSearch.getMemberName() + "%");
            criteria.add(name);
        }

        cq.where(cb.and(criteria.toArray(new Predicate[criteria.size()])));
        TypedQuery<Order> query = em.createQuery(cq).setMaxResults(1000);
        return query.getResultList();
    }

📍 설명

  • JPA CriteriaJPA의 표준 스펙
    JPQL을 자바 코드로 작성할 수 있도록 JPA 가 제공하는 표준 동적 쿼리를 빌드해준다
    그러나,
    실무에서 사용하기 너무 복잡하며 유지보수성이 매우 낮고
    생성될 쿼리문을 떠올리기가 힘들다

❗ 결국 다른 대안이 필요!
동적 쿼리, JPQL 을 위한 해결책이 필요하다고 고민해 나온 라이브러리가 Querydsl 이다

방법 3) Querydsl 로 처리

💻 코드

	/**
     * Querydsl
     * */
    public List<Order> findAll(OrderSearch orderSearch){
        QOrder order = QOrder.order;
        QMember member = QMember.member;
        
        return query
                .select(order)
                .from(order)
                .join(order.member, member)
                .where(statusEq(orderSearch.getOrderStatus()),
                        nameLike(orderSearch.getMemberName()))
                .limit(1000)
                .fetch();
    }
    
    private BooleanExpression statusEq(OrderStatus statusCond){
        if (statusCond == null){
            return null;
        }
        return order.status.eq(statusCond);
    }
    
    private BooleanExpression nameLike(String nameCond){
        if (!StringUtils.hasText(nameCond)){
            return null;
        }
        return member.name.like(nameCond);
    }

강의에서는 다뤄보지 않고 코드만 확인해보겠다


💡 실무에서는 동적쿼리와 복잡한 JPQL을 해결하기 위해 **Springboot, Spring Data JPA, Querydsl** 을 함께 사용하면 생산성을 극대화 하고 컴파일 시점에 문법 오류도 잡아주며 개발을 깔끔하게 할 수 있다고 한다
profile
Just Do IT ------- 🏃‍♀️

0개의 댓글