반응형
해당 글에서는 Spring Boot Data JPA 내에서 기능 중 하나인 Criteria API에 대해 이해하고 기본 동작들에 대해 이해를 돕기 위해 작성한 글입니다.
💡 [참고] JPA 관련해서 구성 내용에 대해 궁금하시면 아래의 글을 참고하시면 도움이 됩니다.
분류 | 링크 |
Spring Boot Data JPA -1: ORM, JPA, Hibernate, QueryDSL 이론 | https://adjh54.tistory.com/421 |
Spring Boot Data JPA -2: 초기 환경 구성 + JpaRepository 활용 방법 | https://adjh54.tistory.com/422 |
Spring Boot Data JPA -3: 상세 JpaRepository 활용 방법(Query Method, @Query, NamedQuery) | https://adjh54.tistory.com/481 |
Spring Boot Data JPA 엔티티 어노테이션 -1 : 테이블 컬럼 단위 | https://adjh54.tistory.com/466 |
Spring Boot Data JPA 엔티티 어노테이션 -2 : 엔티티(테이블)간의 관계 | https://adjh54.tistory.com/477 |
Spring Boot Data JPA FetchType 이해하기 : 즉시/지연로딩 | https://adjh54.tistory.com/476 |
Spring Boot Data JPA + JPQL 활용 방법 | https://adjh54.tistory.com/479 |
Spring Boot Data JPA + Criteria API 활용 방법 | https://adjh54.tistory.com/483 |
Spring Boot Data JPA + QueryDSL 활용 방법-1 : 정의 및 구성요소 | https://adjh54.tistory.com/484 |
Spring Boot Data JPA + QueryDSL 활용 방법-2 : 초기 환경설정 및 활용예시 | https://adjh54.tistory.com/485 |
1) Spring Boot JPA(Java Persistence API)
💡 Spring Boot JPA(Java Persistence API)
- 데이터베이스를 쉽게 다루기 위한 ‘데이터 액세스 기술’로 ORM(Object-Relational Mapping) 기법을 사용하여 자바 애플리케이션에서 사용하는 객체와 관계형 데이터베이스 사이의 매핑을 관리하는 ORM 기술에 대한 API 표준 명세서(인터페이스) 의미합니다. 이 API를 사용하여 개발자가 직접적인 SQL을 작성하지 않고도 데이터베이스에서 데이터를 저장, 업데이트, 삭제, 조회하는 등의 작업을 수행할 수 있게 해 줍니다.
- JPA는 표준화된 API를 제공함으로써, 다양한 ORM 프레임워크(예: Hibernate, EclipseLink, OpenJPA 등)와의 호환성을 보장합니다. 이로 인해 개발자는 특정 ORM 프레임워크에 종속되지 않고 필요에 따라 다른 프레임워크로 쉽게 전환할 수 있습니다.
2) Criteria API
💡 Criteria API
- 자바의 JPA (Java Persistence API) 사양의 일부로서 기존 텍스트로 구성된 JPQL을 빌더 클래스를 사용하여 타입-세이프한 쿼리를 생성하는 API입니다.
- 기존 JPQL의 방식은 텍스트로 SQL를 작성하는 방식이기에 컴파일 타임에 오류를 검출할 수 없었으나, Criteria API에서는 타입-세이프 방식을 통해 컴파일 안정성을 제공하면서 컴파일 타임에 오류를 검출할 수 있습니다.
- 또한, 런타임 시 동적 쿼리를 생성하는 데 유용합니다. 또한 동적 쿼리와 같은 복잡한 쿼리를 프로그래밍 방식으로 구성하여 객체 지향적인 방식으로 데이터베이스 쿼리를 작성할 수 있게 해 줍니다.
- 단 해당 API는 복잡하고 장황한 코드를 만들 수 있어서 이해하고 유지보수하는 데 더 어려울 수 있습니다.
[ 더 알아보기]
💡 타입-세이프(type-safe)란?
- 프로그래밍 언어의 특성을 나타내는 용어로 프로그램이 실행되는 동안(런타임) 데이터의 타입을 체크하여 타입 오류를 방지하는 것을 의미합니다.
- 타입-세이프한 프로그래밍 언어에서는 잘못된 타입의 데이터를 사용하려고 시도하면 컴파일 오류나 런타임 오류가 발생하여 버그를 방지합니다.
1. SQL, JPQL, Criteria API 사용예시
💡 SQL 사용예시
- SQL문에서는 ‘테이블과 컬럼’을 대상으로 쿼리를 작성합니다.
💡 JPQL 사용예시
- JPQL에서는 SQL문과 형태가 비슷합니다. 단, ‘테이블과 컬럼’을 대상으로 하는 것이 아닌 ‘엔티티(클래스)와 변수’를 대상으로 쿼리를 작성합니다.
💡 Criteria API 사용예시
1. CriteriaBuilder 인스턴스를 생성합니다.
2. UserEntity 타입의 CriteriaBuilder를 구성합니다.
3. 데이터를 조회하는 Query의 Root를 지정합니다.
4. Root를 기준으로 전체 데이터를 조회하고 userNm의 값이 123인 값을 확인합니다.
5. 리스트 형태로 결과값을 받습니다.
2. JpaRepositoy, JPQL, Criteria API 비교
💡 JpaRepositoy, JPQL, Criteria API 비교
- JPA를 이용하여 사용되는 기술들에 대해서 각각을 비교해 봅니다.
분류 | JpaRepositoy | JPQL | Criteria API |
타입-세이프 | 불가 | 불가 | 타입-세이프 방식을 사용하여 컴파일 타임에 오류를 검출할 수 있습니다. |
동적 쿼리 생성 | 메소드 쿼리 생성 기능을 통해 동적 쿼리 생성 가능 | 동적 쿼리를 생성하는데 제한적입니다. | 런타임 시 동적 쿼리를 생성하는 데 유용합니다. |
코드의 복잡성 | Repository 인터페이스에 메소드를 선언하는 것만으로 쿼리를 작성할 수 있어 간단합니다. | 직관적이고 간단한 쿼리를 작성할 수 있습니다. | 복잡하고 장황한 코드를 만들 수 있어서 이해하고 유지보수하는 데 어려울 수 있습니다. |
객체 지향 쿼리 작성 | SQL을 직접 작성하는 방식으로, 객체 지향적인 방식이 아닙니다. | SQL을 직접 작성하는 방식으로, 객체 지향적인 방식이 아닙니다. | 객체 지향적인 방식으로 데이터베이스 쿼리를 작성할 수 있습니다. |
3) Criteria API 인터페이스 : javax.persistence.criteria 패키지
💡 Criteria API 인터페이스 : javax.persistence.criteria 패키지
- Criteria API 인터페이스를 이용하기 위해서는 javax.persistence.criteria.* 패키지를 사용합니다.
[더 알아보기]
💡 그럼 JpaRepository나 JPQL의 경우는 무슨 패키지를 사용했을까?
- javax.persistence.* 패키지를 사용하였습니다. Criteria API의 경우에는 javax.persistence 내에 별도의 criteria라는 패키지 내에 속해 있는 javax.persistence.criteria.*를 이용하였습니다.
1. Criteria API 인터페이스 종류
💡 Criteria API 인터페이스 종류
- Criteria API를 활용하는데 이용되는 주요 인터페이스를 확인해 봅니다.
인터페이스 | 분류 | 설명 |
AbstractQuery<T> | 쿼리 인터페이스 | 사용자가 정의한 쿼리를 대표하는 인터페이스 |
CriteriaQuery<T> | 쿼리 인터페이스 | Criteria API로 작성된 쿼리를 표현하는 인터페이스 |
Subquery<T> | 쿼리 인터페이스 | 하위 쿼리를 표현하는 인터페이스 |
CriteriaBuilder | 쿼리 인터페이스 | Criteria API의 시작점으로 모든 쿼리 구성 요소를 생성하는데 사용 |
CollectionJoin<Z,E> | 조인 인터페이스 | 컬렉션에 대한 조인을 표현하는 인터페이스 |
Join<Z,X> | 조인 인터페이스 | Join을 표현하는 인터페이스 |
ListJoin<Z,E> | 조인 인터페이스 | List에 대한 조인을 표현하는 인터페이스 |
MapJoin<Z,K,V> | 조인 인터페이스 | Map에 대한 조인을 표현하는 인터페이스 |
PluralJoin<Z,C,E> | 조인 인터페이스 | 복수형 조인을 표현하는 인터페이스 |
SetJoin<Z,E> | 조인 인터페이스 | Set에 대한 조인을 표현하는 인터페이스 |
Fetch<Z,X> | 조인 인터페이스 | 페치 조인을 표현하는 인터페이스 |
FetchParent<Z,X> | 조인 인터페이스 | 페치 조인의 상위 항목을 표현하는 인터페이스 |
CompoundSelection<X> | 표현식 인터페이스 | 복합 선택을 표현하는 인터페이스 |
CriteriaBuilder.Case<R> | 표현식 인터페이스 | Case 문을 표현하는 인터페이스 |
CriteriaBuilder.Coalesce<T> | 표현식 인터페이스 | Coalesce 문을 표현하는 인터페이스 |
CriteriaBuilder.In<T> | 표현식 인터페이스 | In 문을 표현하는 인터페이스 |
CriteriaBuilder.SimpleCase<C,R> | 표현식 인터페이스 | 간단한 Case 문을 표현하는 인터페이스 |
Expression<T> | 표현식 인터페이스 | 쿼리 내에서 사용되는 표현식을 표현하는 인터페이스 |
Predicate | 표현식 인터페이스 | 조건문을 표현하는 인터페이스 |
Selection<X> | 표현식 인터페이스 | Select 절을 표현하는 인터페이스 |
From<Z,X> | 기타 인터페이스 | From 절을 표현하는 인터페이스 |
Order | 기타 인터페이스 | Order by 절을 표현하는 인터페이스 |
ParameterExpression<T> | 기타 인터페이스 | 쿼리의 매개변수를 표현하는 인터페이스 |
Path<X> | 기타 인터페이스 | 속성 경로를 표현하는 인터페이스 |
Root<X> | 기타 인터페이스 | 쿼리의 Root를 표현하는 인터페이스 |
2. CriteriaQuery 주요 메서드
💡 CriteriaQuery 주요 메서드
- Criteria의 Query를 구성하는 용도로 사용되는 CriteriaQuery 인터페이스의 주요 메서드에 대해 알아봅니다.
분류 | 메서드 | 리턴 타입 | 설명 |
조회 | select(Selection selection) | CriteriaQuery<T> | 쿼리 결과에서 반환할 항목을 지정합니다. |
조회 | distinct(boolean distinct) | CriteriaQuery<T> | 중복 쿼리 결과를 제거할지 여부를 지정합니다. |
조회 | multiselect(List selectionList) | CriteriaQuery<T> | 쿼리 결과에서 반환할 선택 항목을 지정합니다. |
조회 | multiselect(Selection... selections) | CriteriaQuery<T> | 쿼리 결과에서 반환할 선택 항목을 지정합니다. |
조건 | where(Expression restriction) | CriteriaQuery<T> | 지정된 불린 표현식에 따라 쿼리 결과를 제한하도록 쿼리를 수정합니다. |
조건 | where(Predicate... restrictions) | CriteriaQuery<T> | 지정된 제한 술어의 접속에 따라 쿼리 결과를 제한하도록 쿼리를 수정합니다. |
조건 | groupBy(Expression... grouping) | CriteriaQuery<T> | 쿼리 결과에 대한 그룹을 형성하는데 사용되는 표현식을 지정합니다. |
조건 | groupBy(List grouping) | CriteriaQuery<T> | 쿼리 결과에 대한 그룹을 형성하는데 사용되는 표현식을 지정합니다. |
조건 | having(Expression restriction) | CriteriaQuery<T> | 쿼리의 그룹에 대한 제한을 지정합니다. |
조건 | having(Predicate... restrictions) | CriteriaQuery<T> | 지정된 제한 술어의 접속에 따라 쿼리의 그룹에 대한 제한을 지정합니다. |
정렬 | orderBy(List o) | CriteriaQuery<T> | 쿼리 결과를 정렬하는데 사용되는 정렬 표현식을 지정합니다. |
정렬 | orderBy(Order... o) | CriteriaQuery<T> | 쿼리 결과를 정렬하는데 사용되는 정렬 표현식을 지정합니다. |
반환 | getParameters() | Set<ParameterExpression<?>> | 쿼리의 매개변수를 반환합니다. |
반환 | getOrderList() | List<Order> | 우선 순위에 따라 정렬 표현식을 반환합니다. |
3. CriteriaQuery 전체 메서드
💡 CriteriaQuery 전체 메서드
- 주요 메서드 외에 사용되는 모든 메서드에 대해 아래의 글을 확인하시면 도움이 됩니다.
4) Criteria API 수행과정
단계 | 필요 API 인터페이스 | 설명 |
CriteriaBuilder & CriteriaQuery 구성 | CriteriaBuilder, CriteriaQuery | Criteria API의 시작점은 CriteriaBuilder를 사용하여 모든 쿼리 구성 요소를 생성하고, CriteriaQuery로 쿼리를 표현하는 객체를 구성하여 쿼리 수행 준비를 완료한다. |
FROM 절 구성 | Root | '엔티티'에 접근하여 쿼리를 시작하게 된다. 이는 SQL문의 FROM 절에 해당한다. |
SELECT 절 구성 | Selection | SQL 문의 컬럼 조회 부분을 구성한다. 이 부분에서는 엔티티 내의 필드를 조회하는 형태로 수행된다. |
WHERE 절 구성 | Predicates | 쿼리의 결과를 제한하는 조건문을 구성하는 부분이다. 조건 술어의 접속에 따라 쿼리 결과의 제한이 지정된다. |
ORDER 절 구성 | Order | 쿼리 결과를 정렬하는데 사용되는 정렬 표현식을 지정하는 부분이다. |
쿼리 생성 및 결과값 반환 | - | 마지막으로, 위의 모든 구성 요소를 사용하여 최종 쿼리를 생성하고, 그 결과를 반환한다. |
1. CriteriaBuilder & CriteriaQuery 구성 : Criteria Queries
💡 CriteriaBuilder & CriteriaQuery 구성
- CriteriaBuilder를 통해 Criteria API의 시작점으로 모든 쿼리 구성 요소를 생성하는 데 사용하며 인스턴스를 생성합니다.
- CriteriaQuery를 통해 Criteria API로 작성된 쿼리를 표현하는 객체를 구성하여 쿼리를 수행할 준비를 완료합니다.
인터페이스 | 분류 | 설명 |
CriteriaQuery<T> | 쿼리 인터페이스 | Criteria API로 작성된 쿼리를 표현하는 인터페이스 |
CriteriaBuilder | 쿼리 인터페이스 | Criteria API의 시작점으로 모든 쿼리 구성 요소를 생성하는데 사용 |
@Repository
public class UserRepository {
@PersistenceContext
private EntityManager em;
public List<UserEntity> selectUserList(String userNm) {
// 1. CriteriaBuilder 인스턴스를 생성합니다.
CriteriaBuilder cb = em.getCriteriaBuilder();
// 2. UserEntity 타입의 CriteriaBuilder를 구성합니다.
CriteriaQuery<UserEntity> cq = cb.createQuery(UserEntity.class);
}
}
2. FROM 절 구성 : Query Roots
💡 FROM 절 구성 : Query Root
- SQL문에서 FROM절에 해당하는 부분을 구성합니다. 해당 부분에서는 ‘엔티티’에 접근하여 시작되는 부분 수행이 됩니다.
3. UserEntity클래스를 기준으로 CriteriaQuery 내에 from 절을 구성합니다.
인터페이스 | 분류 | 설명 |
Root<X> | 기타 인터페이스 | 쿼리의 Root를 표현하는 인터페이스 |
@Repository
public class UserRepository {
@PersistenceContext
private EntityManager em;
public List<UserEntity> selectUserList(String userNm) {
// 1. CriteriaBuilder 인스턴스를 생성합니다.
CriteriaBuilder cb = em.getCriteriaBuilder();
// 2. UserEntity 타입의 CriteriaBuilder를 구성합니다.
CriteriaQuery<UserEntity> cq = cb.createQuery(UserEntity.class);
// 3. 데이터를 조회하는 Query의 Root를 지정합니다.
Root<UserEntity> root = cq.from(UserEntity.class);
}
}
[ 더 알아보기 ]
💡 쿼리 루트(Query Root)
- SQL문에서 FROM절에 해당하는 부분에 사용이 되며 ‘엔티티’에 접근하여 쿼리가 시작이 됩니다.
- 예를 들어, UserEntity 클래스를 기준으로 CriteriaQuery 내에 from 절을 구성하는 것이 쿼리 루트의 역할입니다.
3. SELECT 절 구성 : Selection
💡 SELECT 절 구성 : Selection
- SQL문에서 컬럼을 조회하는 부분을 구성합니다. 해당 부분에서는 엔티티 내에서 필드를 조회하는 형태로 수행을 합니다.
4. select 메서드를 이용하여 root(엔티티) 전체를 조회하는 방식 : 전체 조회
5. multiselect 메서드를 이용하여 root(엔티티) 내에 필드를 조회하는 방식 : 일부 필드 조회
분류 | 메서드 | 리턴타입 | 설명 |
조회 | select(Selection selection) | CriteriaQuery<T> | 쿼리 결과에서 반환할 항목을 지정합니다. |
조회 | multiselect(List selectionList) | CriteriaQuery<T> | 쿼리 결과에서 반환할 선택 항목을 지정합니다. |
조회 | multiselect(Selection... selections) | CriteriaQuery<T> | 쿼리 결과에서 반환할 선택 항목을 지정합니다. |
@Repository
public class UserRepository {
@PersistenceContext
private EntityManager em;
/**
* 사용자 리스트를 조회합니다.
*
* @param userNm
* @return
*/
public List<UserEntity> selectUserList(UserDto userDto) {
// 1. CriteriaBuilder 인스턴스를 생성합니다.
CriteriaBuilder cb = em.getCriteriaBuilder();
// 2. UserEntity 타입의 CriteriaBuilder를 구성합니다.
CriteriaQuery<UserEntity> cq = cb.createQuery(UserEntity.class);
// 3. 데이터를 조회하는 Query의 Root를 지정합니다.
Root<UserEntity> root = cq.from(UserEntity.class);
// 4. [전체 조회] CreateQuery 내에서 root라는 엔티티 전체 검색을 합니다.
cq = cq.select(root);
// OR
// 5. [일부 필드 조회] Root 내에서 특정 컬럼을 찾아서 일부만 조회합니다.
cq = cq.multiselect(
root.get("userId"),
root.get("userNm")
);
}
}
4. WHERE 절 구성 : Predicates
💡 WHERE 절 구성 : Predicates
- SQL문에서 컬럼을 조회할 때 조건을 추가하는 부분을 구성합니다. 해당 부분에서는 CriteriaQuery로 조회할 엔티티와 엔티티 필드를 지정한 상태에서 이어서 조건문을 추가합니다.
5. 이전에 구성하였던 CriteriaQuery에 .where() 구문을 추가하여 구성합니다.
OR
6. Predicate 객체로 구성하며, CriteriaQuery 내에 .where() 구문으로 추가 구성합니다.
분류 | 메서드 | 리턴타입 | 설명 |
조건 | where(Expression restriction) | CriteriaQuery<T> | 지정된 불린 표현식에 따라 쿼리 결과를 제한하도록 쿼리를 수정합니다. |
조건 | where(Predicate... restrictions) | CriteriaQuery<T> | 지정된 제한 술어의 접속에 따라 쿼리 결과를 제한하도록 쿼리를 수정합니다. |
조건 | groupBy(Expression... grouping) | CriteriaQuery<T> | 쿼리 결과에 대한 그룹을 형성하는데 사용되는 표현식을 지정합니다. |
조건 | groupBy(List grouping) | CriteriaQuery<T> | 쿼리 결과에 대한 그룹을 형성하는데 사용되는 표현식을 지정합니다. |
조건 | having(Expression restriction) | CriteriaQuery<T> | 쿼리의 그룹에 대한 제한을 지정합니다. |
조건 | having(Predicate... restrictions) | CriteriaQuery<T> | 지정된 제한 술어의 접속에 따라 쿼리의 그룹에 대한 제한을 지정합니다. |
import com.adjh.multiflex.jpa.dto.UserDto;
import com.adjh.multiflex.jpa.entity.UserEntity;
import org.springframework.stereotype.Repository;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.List;
@Repository
public class UserRepository {
@PersistenceContext
private EntityManager em;
/**
* 사용자 리스트를 조회합니다.
*
* @return
*/
public List<UserEntity> selectUserList(UserDto userDto) {
// 1. CriteriaBuilder 인스턴스를 생성합니다.
CriteriaBuilder cb = em.getCriteriaBuilder();
// 2. UserEntity 타입의 CriteriaBuilder를 구성합니다.
CriteriaQuery<UserEntity> cq = cb.createQuery(UserEntity.class);
// 3. 데이터를 조회하는 Query의 Root를 지정합니다.
Root<UserEntity> root = cq.from(UserEntity.class);
// 4. [전체 조회] CreateQuery 내에서 root라는 엔티티 전체 검색을 합니다.
cq = cq.select(root);
// 5. CreateQuery 내에 조건절을 추가합니다.
cq = cq.where(
root.get("userSt").in("A", "I", "D"), // userSt IN ("A", "I", "D")
cb.equal(root.get("delYn"), false), // delYn = false
cb.equal(root.get("userNm"), userDto.getUserNm()), // userNm = #{userNm}
cb.equal(root.get("userId"), userDto.getUserId()) // userId = #{userId}
);
// OR
// 6. Predicate 객체로 분리하여 조건절을 구성합니다.
Predicate pWhere = cb.and(
cb.in(root.get("userId")).value(userDto.getUserId()), // userId = #{userId}
cb.equal(root.get("userNm"), userDto.getUserNm()) // userNm = #{userNm}
);
cq = cq.where(pWhere);
}
}
5. ORDER 절 구성 : Order
💡 ORDER 절 구성 : Order
- SQL문에서 정렬을 수행하는 부분을 구성합니다. 해당 부분에서는 CriteriaQuery 내에 특정 필드를 기반으로 정렬을 수행합니다.
6. userId는 오름차순, userNm은 내림차순으로 정렬합니다.
분류 | 메서드 | 리턴타입 | 설명 |
정렬 | orderBy(List o) | CriteriaQuery<T> | 쿼리 결과를 정렬하는데 사용되는 정렬 표현식을 지정합니다. |
정렬 | orderBy(Order... o) | CriteriaQuery<T> | 쿼리 결과를 정렬하는데 사용되는 정렬 표현식을 지정합니다. |
import com.adjh.multiflex.jpa.dto.UserDto;
import com.adjh.multiflex.jpa.entity.UserEntity;
import org.springframework.stereotype.Repository;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import java.util.List;
@Repository
public class UserRepository {
@PersistenceContext
private EntityManager em;
/**
* 사용자 리스트를 조회합니다.
*
* @return
*/
public List<UserEntity> selectUserList(UserDto userDto) {
// 1. CriteriaBuilder 인스턴스를 생성합니다.
CriteriaBuilder cb = em.getCriteriaBuilder();
// 2. UserEntity 타입의 CriteriaBuilder를 구성합니다.
CriteriaQuery<UserEntity> cq = cb.createQuery(UserEntity.class);
// 3. 데이터를 조회하는 Query의 Root를 지정합니다.
Root<UserEntity> root = cq.from(UserEntity.class);
// 4. [전체 조회] CreateQuery 내에서 root라는 엔티티 전체 검색을 합니다.
cq = cq.select(root);
// 5. CreateQuery 내에 조건절을 추가합니다.
cq = cq.where(
root.get("userSt").in("A", "I", "D"), // userSt IN ("A", "I", "D")
cb.equal(root.get("delYn"), false), // delYn = false
cb.equal(root.get("userNm"), userDto.getUserNm()), // userNm = #{userNm}
cb.equal(root.get("userId"), userDto.getUserId()) // userId = #{userId}
);
// 6. CreateQuery 내에 정렬 부분을 추가합니다.
cq.orderBy(
cb.asc(root.get("userId")),
cb.desc(root.get("userNm"))
);
}
}
6. 쿼리 생성 및 결과값 반환 : Executing Queries
💡 쿼리 생성 및 결과값 반환 : Executing Queries
- 최종 구성한 SQL문을 쿼리로 생성하고 반환받을 결과값을 단건(getSingleResult) 혹은 다건(getResultList)으로 받을지에 대해 지정하는 단계입니다.
7. 구성한 CriteriaQuery를 기반으로 쿼리를 생성하여 다건으로 반환받습니다.
import com.adjh.multiflex.jpa.dto.UserDto;
import com.adjh.multiflex.jpa.entity.UserEntity;
import org.springframework.stereotype.Repository;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import java.util.List;
@Repository
public class UserRepository {
@PersistenceContext
private EntityManager em;
/**
* 사용자 리스트를 조회합니다.
*
* @return
*/
public List<UserEntity> selectUserList(UserDto userDto) {
// 1. CriteriaBuilder 인스턴스를 생성합니다.
CriteriaBuilder cb = em.getCriteriaBuilder();
// 2. UserEntity 타입의 CriteriaBuilder를 구성합니다.
CriteriaQuery<UserEntity> cq = cb.createQuery(UserEntity.class);
// 3. 데이터를 조회하는 Query의 Root를 지정합니다.
Root<UserEntity> root = cq.from(UserEntity.class);
// 4. [전체 조회] CreateQuery 내에서 root라는 엔티티 전체 검색을 합니다.
cq = cq.select(root);
// 5. CreateQuery 내에 조건절을 추가합니다.
cq = cq.where(
root.get("userSt").in("A", "I", "D"), // userSt IN ("A", "I", "D")
cb.equal(root.get("delYn"), false), // delYn = false
cb.equal(root.get("userNm"), userDto.getUserNm()), // userNm = #{userNm}
cb.equal(root.get("userId"), userDto.getUserId()) // userId = #{userId}
);
// 6. CreateQuery 내에 정렬 부분을 추가합니다.
cq.orderBy(
cb.asc(root.get("delYn")),
cb.desc(root.get("userNm"))
);
// 7. 구성한 CriteriaQuery를 기반으로 쿼리를 생성하여 다건으로 반환받습니다.
return em.createQuery(cq).getResultList();
}
}
5) Criteria API 한계
1. 가독성
💡 가독성
- Criteria API를 사용하면 코드가 복잡해질 수 있습니다. 복잡한 쿼리를 작성할 때, SQL 문을 직접 작성하는 것이 더 간단하고 이해하기 쉬울 수 있습니다.
- JPQL과 비교하여 소스코드를 확인해 봤을 때도 상대적으로 복잡함을 확인할 수 있습니다.
💡 JPQL로 구성한 SQL문입니다.
💡 Criteria API로 구성한 SQL문입니다.
2. 복잡성
💡 복잡성
- Criteria API를 사용하면 쿼리를 작성하는 데 필요한 인터페이스와 메서드가 많아질 수 있고 이는 코드를 더 복잡하게 만들 수 있습니다.
💡 사용예시
- 동적 정렬을 하는 코드입니다. 해당 코드를 확인해 보더라도 처리를 위해 많은 코드가 들어가서 복잡함을 확인할 수 있습니다.
3. 성능
💡 성능
- Criteria API는 실행 시점에 쿼리를 생성하고, 이로 인해 일부 상황에서 성능 저하가 발생할 수 있습니다.
- 일반적은 MyBatis를 이용한 방법이나 Named Query를 이용하는 방법은 초기에 SQL문을 가져오기에 불러오는 시간이 상대적으로 짧습니다.
- 그러나 Criteria API의 경우에는 메서드 호출 시 불러오기에 성능 저하가 발생할 수 있습니다.
4. 지원범위
💡 지원범위
- JPA 표준에서 제공하는 기능만 지원하므로, 특정 데이터베이스에 특화된 기능을 사용할 수 없습니다.
- PostgreSQL, MySQL, Oracle 등 각각의 데이터베이스에서 지원하는 기능을 직접적으로 사용이 불가능합니다.
오늘도 감사합니다. 😀
반응형