JKROH 2023. 4. 4. 16:17
반응형
해당 강의는 코드 위주로 진행됩니다. 실질적인 객체 처리를 담당하는 코드는 링크에서 확인하시길 바랍니다.

* Criteria

  • 코드로 쿼리를 구성하는 API
    • JPQL 대신 자바 코드 사용
            CriteriaBuilder cb = em.getCriteriaBuilder();
            CriteriaQuery<Review> cq = cb.createQuery(Review.class);
            
            /**
            *  cq를 이용해서 쿼리 구성
            */
            Root<Review> root = cq.from(Review.class);
            cq.select(root);
            
            Predicate p = cb.conjunction();
            if (hotelId != null) {
                p = cb.and(p, cb.equal(root.get("hotelId"), hotelId));
            }
            p = cb.and(p, cb.greaterThan(root.get("created"), LocalDateTime.now().minusDays(10)));
            if (mark >= 0) {
                p = cb.and(p, cb.ge(root.get("mark"), mark));
            }
            
            cq.where(p);
            cq.orderBy(cb.asc(root.get("hotelId"));
            
            // select r from Review r where r.hotelId = 'H-001'
            
            TypedQuery<Review> query = em.createQuery(cq); // cq를 넣어줘서 TypedQuery 형성
            query.setFirstResult(4); // 0부터 시작
            query.setMaxResults(4);
            List<Review> reviews = query.getResultList();

 

* 기본 사용법

  1. CriteriaBuilder 생성 => CriteriaBuilder cb = em.getCriteriaBuilder();
  2. CreateQuery 생성 - 파라미터는 쿼리 결과 타입 => CriteriaQuery<Review> cq = cb.createQuery(Review.class);
  3. CQ#from 으로 from 절 엔티티 지정 - Root 타입을 이용해서 엔티티 속성 접근
    => Root<Review> root = cq.from(Review.class);
  4. CQ#select 로 select 대상 지정 => cq.select(root);
  5. CE#where 로 조건 지정 - cb를 이용해서 Predicate 생성 => cq.where(cb.equal(root.get("hotelId"), "H-001")); 
  6. CQ#orderBy로 정렬 순서 지정 - cb를 이용해서 Order 생성 => cq.orderBy(cb.asc(roote.get("id))); 
  7. cq를 이용해서 TypedQuery 생성 => TypedQuery<Review> query = em.createQuery(cq);

 

* 검색 조건 지정

  • CQ#where 메서드에 검색 조건 전달
  • 검색 조건은 CriteriaBuilder를 이용해서 생성
    • ex - 같음 조건은 CB#equal()로 생성
  • 검색 조건에 사용할 엔티티 속성은 Root#get() 메서드로 구함.
Root<Review> root = cq.from(Review.class);

//생성 조건 : Review의 hotelId가 'H-001'과 같음 -> where review.hotelId = 'H-001'
Predicate predicate = cq.where(cb.equal(root.get("hotelId"), "H-001")); 

cq.where(predicate);
  • 조합
    • CB#and, CB#or로 조건 조합
Predicate p1 = ~~~ 조건;
Predicate p2 = ~~~ 조건;
Predicate predicate = cb.and(p1, p2);

cq.where(predicate); -> where p1 and p2

 

* 정렬 순서 지정

  • CQ#orderBy로 정렬을 지정한다.
  • CB#asc(), CB#desc()로 정렬 정보(Order)를 생성한다.
    • 정렬 대상 속성은 Root#get()으로 구한다.
Order orderId = cb.asc(root.get("id"));
cq.orderBy(orderId);
  • 한 개 이상 정렬 지정이 가능하다.
 cq.orderBy(
           cb.asc(root.get("hotelId")),
           cb.desc(root.get("id"))
    );

 

* CriteriaBuilder의 주요 조건 생성 메서드

 

* Root#get()과 제네릭 타입

  • <Y>Path<Y> get(String attributeName)
  • in() 조건 생성할 때 타입 파라미터를 지정하면 유용하다.
// mark 속성(Y)이 int 타입
CriteriaBuilder.In<Object> markCond = cb.in(root.get("mark")); -> int지정을 안했으니 Object로 받음 컴파일은 되
markCond.value(1).value("a"); // Object니까 컴파일은 됨, 그러나 런타임(쿼리 생성 시점)에 에러

//mark 속성이 int 타입
CriteriaBuilder.In<Object> markCond = cb.in(root.<Integer>get("mark")); -> Integer 지정
markCond.value(1).value("a"); // 컴파일 시점에 에러

 

* Crietria 사용 이점

  • 타입에 안전한 코드를 만들 수 있음 (위의 제네릭 타입 파라미터 예시)
  • 동적인 검색 조건 지정 가능
            /**
            * 조건에 따라 계속해서 조건을 붙여나감
            */
            Predicate p = cb.conjunction();
            if (hotelId != null) {
                p = cb.and(p, cb.equal(root.get("hotelId"), hotelId));
            }
            p = cb.and(p, cb.greaterThan(root.get("created"), LocalDateTime.now().minusDays(10)));
            if (mark >= 0) {
                p = cb.and(p, cb.ge(root.get("mark"), mark));
            }
            cq.where(p);

 

* 그 외 이런게 있다/된다

  • JPQL과 같다.
  • JPQL 함수 대신 JPA 함수를 쓸 수 있다. 하지만, 사실상 별로 사용할 일은 없다.

 

* 정리

  • 주의 - 다음 경우는 Criteria 말고 일반 쿼리 사용 고려
    • JPQL과 같다.
  • 그 외
    • 굳이 연관 + 쿼리를 사용하고 싶다면 N+1문제, fetch 조인을 추가로 학습하자.
    • 단순하지 않은 목록 조회 / 상세 조회는 그냥 SQL을 쓰자(CQRS)
반응형