[JPA] 6. 주문 도메인 개발 - 주문 검색 기능 개발

2025. 5. 20. 11:06·BE/스프링부트와 JPA 활용
728x90

JPA에서 동적쿼리를 어떻게 해결해야 하는가?

검색 조건 파라미터 OrderSearch

@Getter @Setter
public class OrderSearch {

    private String memberName; // 회원 이름
    private OrderStatus orderStatus; // 주문 상태(Order, Cancle)
}

주문 리포지토리에 검색 추가

// 주문 조회 -> 검색이 되어야 함
public List<Order> findAll(OrderSearch orderSearch){
    return em.createQuery("select o from Order o join o.member" +
                    "where o.status = :status" +
                    "and m.name like :name", Order.class)
            .setParameter("status", orderSearch.getMemberName())
            .setMaxResults(1000) // 최대 1000건
            .getResultList();
}

다만, 위의 코드는 값이 전부 있다는 가정을 했을 때의 코드이다.

status가 없거나 name이 없을 때에는

// 주문 조회 -> 검색이 되어야 함
public List<Order> findAll(OrderSearch orderSearch){
    return em.createQuery("select o from Order o join o.member", Order.class)
            .setMaxResults(1000) // 최대 1000건
            .getResultList();
}

이런식으로 리스트 전체가 출력되어야 하는데 이렇게 상황에 따라 쿼리가 달라지는 경우를 동적쿼리라고 한다.

즉, findAll(OrderSearch orderSearch) 메서드는 검색 조건에 동적으로 쿼리를 생성해서 주문 엔티티를 조회한다.

동적쿼리 문제를 해결하기 위해 JPQL을 사용할 수 있다.

JPQL

JPQL이란? Java Persistence Query Language, 객체지향 쿼리로 JPA가 지원하는 다양한 쿼리 방법 중 하나

  • SQL은 테이블 중심
  • JPQL은 엔티티 중심
SELECT * 
FROM orders o 
JOIN member m ON o.member_id = m.id 
WHERE m.name = 'kim';
String jpql = "SELECT o FROM Order o WHERE o.member.name = :name";

실제로 실행될 때는 JPA가 내부적으로 SQL로 바꿔서 DB에 보내준다!

public List<Order> findAll(OrderSearch orderSearch){

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

    // 주문 상태 검색
    if(orderSearch.getOrderStatus() != null){ // 주문상태가 있을 경우(Order, Cancle)
        if(isFirstCondition){   // 쿼리에 넣는 첫 조건
            jpql += "where";
            isFirstCondition = false;
        }else{  // 아니면 and로 조건 이어가기
            jpql += "and";
        }
    }

    // 회원 이름 검색
    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); // 최대 1000건

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

    List<Order> resultList = query.getResultList();
    return resultList;
}

JPQL은 객체 지향 쿼리를 지원하지만, 문자열로 직접 조합하는 방식은 번거롭고 오류 발생 가능성이 크다.
특히 조건이 늘어나면서 where, and, setParameter 등을 수동으로 관리해야 하기 때문에 복잡한 동적 쿼리를 다루기엔 한계가 있다.

그럼 어떤 방식이 좋을까?

JPA Criteria로 처리

Criteira란? JPQL을 자바 코드로 작성하도록 도와주는 빌더 클래스 API

// 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> criteira = new ArrayList<>();

    if(orderSearch.getOrderStatus() != null){
        Predicate status = cb.equal(o.get("status"), orderSearch.getOrderStatus());
        criteira.add(status);
    }

    if(StringUtils.hasText(orderSearch.getMemberName())){
        Predicate name = cb.like(m.get("name"),"%"+orderSearch.getMemberName()+"%");
        criteira.add(name);
    }

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

JPA Criteria는 JPA 표준 스펙이지만 실무에서 사용하기에 복잡해서 유지보수가 어렵다는 단점이 있다. 

그럼 어떻게 해결할 수 있을까?!? 바로 Querydsl !

728x90

'BE > 스프링부트와 JPA 활용' 카테고리의 다른 글

[JPA] 7. 웹 계층 개발 - 홈 화면과 레이아웃  (0) 2025.05.20
[JPA] 6. 주문 도메인 개발 - 리포지토리/서비스/테스트  (0) 2025.05.17
[JPA] 6. 주문 도메인 개발 - 주문, 주문상품 엔티티 개발  (1) 2025.05.14
[JPA] 5. 상품 도메인 개발  (0) 2025.05.14
[JPA] 4. 회원 도메인 개발  (0) 2025.05.13
'BE/스프링부트와 JPA 활용' 카테고리의 다른 글
  • [JPA] 7. 웹 계층 개발 - 홈 화면과 레이아웃
  • [JPA] 6. 주문 도메인 개발 - 리포지토리/서비스/테스트
  • [JPA] 6. 주문 도메인 개발 - 주문, 주문상품 엔티티 개발
  • [JPA] 5. 상품 도메인 개발
DROPDEW
DROPDEW
💻 Developer | 기록하지 않으면 존재하지 않는다
  • DROPDEW
    제 2장 1막
    DROPDEW
  • 전체
    오늘
    어제
    • Dev (440)
      • App·Android (1)
      • BE (50)
        • HTTP 웹 기본 지식 (8)
        • 스프링 입문 - 코드로 배우는 스프링 부트, 웹 .. (12)
        • 스프링부트와 JPA 활용 (11)
        • 스프링부트 시큐리티 & JWT (0)
        • 실전 자바 기본, 중급 (1)
        • PHP (11)
      • FE·Client (23)
        • HTML (1)
        • React (19)
        • Unity (1)
      • Data (28)
        • AI (7)
        • Bigdata (6)
        • Database (1)
        • Python (0)
        • 빅데이터분석기사 (13)
      • Infra (1)
      • Activity (5)
        • Education (0)
        • Intern (0)
        • 리모트 인턴십 6기 (3)
        • 구름톤 유니브 4기 (0)
        • SW교육기부단 15기 (0)
        • SK AI Dream Camp (2)
      • CS (8)
      • 취준 (13)
        • 자격증 (4)
        • 인적성·NCS (6)
        • 코테·필기·면접 후기 (3)
      • 코테 (270)
        • Algorithm (222)
        • SQL (35)
        • 정리 (13)
      • 인사이트 (27)
        • 회고 (0)
        • 금융경제뉴스 (7)
        • 금융용어·지식 (2)
        • 북마크 (7)
  • 블로그 메뉴

    • 홈
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    브루트포스 알고리즘
    그리디알고리즘
    티스토리챌린지
    투포인터
    그래프이론
    너비우선탐색
    시뮬레이션
    백준
    구현
    문자열
    최단경로
    오블완
    자료구조
    수학
    그래프탐색
    이분탐색
    누적합
    매개변수탐색
    정렬
    다이나믹프로그래밍
  • 최근 댓글

  • 최근 글

  • 250x250
  • hELLO· Designed By정상우.v4.10.3
DROPDEW
[JPA] 6. 주문 도메인 개발 - 주문 검색 기능 개발
상단으로

티스토리툴바