Spring/Spring Data JPA

[자바 ORM 표준 JPA 프로그래밍] 10-2. 객체지향 쿼리 언어: Criteria

noahkim_ 2025. 4. 24. 10:39

김영한 님의 "자바 ORM 표준 JPA 프로그래밍" 책을 정리한 포스팅 입니다.

 

1. Criteria

  • JPQL을 편하게 작성하도록 도와주는 빌더 클래스 모음
  • 컴파일 시점에 오류를 발견할 수 있음
  • 동적 쿼리를 작성하기 편함

 

2. 기초

항목 설명
CriteriaBuilder
Criteria 쿼리 생성을 위한 시작점
CriteriaQuery
쿼리 전체 구조를 담는 객체
Root
조회의 시작점 (FROM 절)
Predicate
WHERE 조건 표현
Order
ORDER BY 정렬 표현

 

예제

더보기
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<User> query = cb.createQuery(User.class);
Root<User> u = query.from(User.class);

query.select(u)
        .where(cb.le(u.get("id"), 100))
        .orderBy(cb.desc(u.get("name")));

 

3. 쿼리

분류 항목 설명
쿼리 생성
select, distinct, multiselect
다양한 형태의 SELECT 문 지원
construct() DTO 반환
tuple()
튜플 형태의 결과 반환
집합
groupBy()
GROUP BY 절 구성
having()
HAVING 절 구성
정렬 orderBy() 정렬 조건 추가
조인 join(), fetch()
JOIN 및 FETCH JOIN 구성
서브쿼리
subquery() 서브 쿼리 생성
correlate()
메인 쿼리의 엔티티를 서브 쿼리로 가져옴
IN 식 cb.in(...).value(...) IN 절 처리
CASE selectCase().when().otherwise() 조건 분기
파라미터 cb.parameter()
바인딩 파라미터 정의
함수 호출 cb.function()
네이티브 SQL 함수 호출
동적 쿼리 Predicate 조건 누적
조건을 유동적으로 조립 가능

 

예제) 쿼리 생성

더보기
@Test
@Transactional
void select() {
    CriteriaBuilder cb = entityManager.getCriteriaBuilder();
    CriteriaQuery<User> query = cb.createQuery(User.class);
    Root<User> u = query.from(User.class);

    // SELECT u FROM User as u WHERE u.id <= 100 ORDER BY u.name DESC
    query.select(u)
            .where(cb.le(u.get("id"), 100))
            .orderBy(cb.desc(u.get("name")));

    TypedQuery<User> typedQuery = entityManager.createQuery(query);
    List<User> resultList = typedQuery.getResultList();

    for (User row : resultList) {
        System.out.println("id: " + row.getId());
        System.out.println("name: " + row.getName());
        System.out.println("team id: " + row.getTeam().getName());

        System.out.println();
    }
}
@Test
@Transactional
void multiselect() {
    CriteriaBuilder cb = entityManager.getCriteriaBuilder();
    CriteriaQuery<User> query = cb.createQuery(User.class);
    Root<User> u = query.from(User.class);

    // SELECT DISTINCT u.id, u.get FROM User u
    query.multiselect(u.get("id"), u.get("name")).distinct(true);

    TypedQuery<User> typedQuery = entityManager.createQuery(query);
    List<User> resultList = typedQuery.getResultList();

    for (User row : resultList) {
        System.out.println("id: " + row.getId() + ", name: " + row.getName());
    }
}
@Test
@Transactional
void constructor() {
    CriteriaBuilder cb = entityManager.getCriteriaBuilder();
    CriteriaQuery<UserDto> query = cb.createQuery(UserDto.class);
    Root<User> u = query.from(User.class);

    query.select(cb.construct(UserDto.class, u.get("id"), u.get("name")));

    TypedQuery<UserDto> typedQuery = entityManager.createQuery(query);
    List<UserDto> resultList = typedQuery.getResultList();

    for (UserDto row : resultList) {
        System.out.println("id: " + row.getId() + ", name: " + row.getName());
    }
}
@Test
@Transactional
void tuple() {
    CriteriaBuilder cb = entityManager.getCriteriaBuilder();
    CriteriaQuery<Tuple> query = cb.createQuery(Tuple.class);
    Root<User> u = query.from(User.class);

    // SELECT DISTINCT u.id as id, u.name as username FROM User u
    query.multiselect(
            u.get("id").alias("id"),
            u.get("name").alias("username")
    );

    TypedQuery<Tuple> typedQuery = entityManager.createQuery(query);
    List<Tuple> resultList = typedQuery.getResultList();

    for (Tuple row : resultList) {
        Integer id = row.get("id", Integer.class);
        String username = row.get("username", String.class);

        System.out.println("id: " + id + ", name: " + username);
    }
}

 

예제) 집합

더보기
@Test
@Transactional
void aggregation() {
    CriteriaBuilder cb = entityManager.getCriteriaBuilder();
    CriteriaQuery<Tuple> query = cb.createQuery(Tuple.class);
    Root<User> u = query.from(User.class);

    query.multiselect(
            u.get("team").get("name").alias("team_name"),
            cb.count(u).alias("cnt")
        )
        .groupBy(u.get("team").get("name"));

    TypedQuery<Tuple> typedQuery = entityManager.createQuery(query);
    List<Tuple> resultList = typedQuery.getResultList();

    for (Tuple row : resultList) {
        System.out.println("team name: " + row.get("team_name", String.class) + ", count: " + row.get("cnt", Long.class));
    }
}

 

예제) 정렬

 

예제) 조인 

더보기
@Test
@Transactional
void join() {
    CriteriaBuilder cb = entityManager.getCriteriaBuilder();
    CriteriaQuery<Tuple> query = cb.createQuery(Tuple.class);
    Root<User> u = query.from(User.class);
    Join<User, Team> t = u.join("team", JoinType.INNER);

    query.multiselect(
        u.get("name").alias("user_name"),
        t.get("name").alias("team_name")
    );

    TypedQuery<Tuple> typedQuery = entityManager.createQuery(query);
    List<Tuple> resultList = typedQuery.getResultList();

    for (Tuple row : resultList) {
        System.out.println("team name: " + row.get("team_name", String.class) + ", user name: " + row.get("user_name", String.class));
    }
}

 

예제) 서브쿼리

더보기
@Test
@Transactional
void subquery() {
    CriteriaBuilder cb = entityManager.getCriteriaBuilder();
    CriteriaQuery<User> query = cb.createQuery(User.class);
    Root<User> user = query.from(User.class);

    Subquery<Double> subquery = query.subquery(Double.class);
    Root<User> subUser = subquery.from(User.class);
    subquery.select(cb.avg(subUser.get("age")));

    query
        .select(user)
        .where(cb.lessThan(user.get("age"), subquery));

    TypedQuery<User> typedQuery = entityManager.createQuery(query);
    List<User> resultList = typedQuery.getResultList();

    for (User data : resultList) System.out.println(data);
}

 

예제) IN

더보기
@Test
@Transactional
void in() {
    CriteriaBuilder cb = entityManager.getCriteriaBuilder();
    CriteriaQuery<User> query = cb.createQuery(User.class);
    Root<User> user = query.from(User.class);

    List<String> names = List.of("Stephen Curry", "Jimmy Butler");

    query
        .select(user)
        .where(user.get("name").in(names));

    TypedQuery<User> typedQuery = entityManager.createQuery(query);
    List<User> resultList = typedQuery.getResultList();

    for (User data : resultList) System.out.println(data);
}

 

예제) CASE

더보기
@Test
@Transactional
void case_keyword() {
    CriteriaBuilder cb = entityManager.getCriteriaBuilder();
    CriteriaQuery<Tuple> query = cb.createQuery(Tuple.class);
    Root<User> user = query.from(User.class);

    Expression<Object> ageLabel = cb.selectCase()
                    .when(cb.lt(user.get("age"), 20), "Teen")
                    .when(cb.between(user.get("age"), 20, 29), "20s")
                    .otherwise("30+");

    query.multiselect(
        user.get("name").alias("user_name"),
        ageLabel.alias("age_group")
    );

    TypedQuery<Tuple> typedQuery = entityManager.createQuery(query);
    List<Tuple> resultList = typedQuery.getResultList();

    for (Tuple row : resultList) {
        System.out.println("[" + row.get("age_group", String.class) + "] " + row.get("user_name", String.class));
    }
}

 

예제) 파라미터 바인딩

더보기
@Test
@Transactional
void parameter_binding() {
    CriteriaBuilder cb = entityManager.getCriteriaBuilder();
    CriteriaQuery<User> query = cb.createQuery(User.class);
    Root<User> user = query.from(User.class);

    query.select(user).where(cb.equal(user.get("name"), cb.parameter(String.class, "name")));

    TypedQuery<User> typedQuery = entityManager.createQuery(query);
    typedQuery.setParameter("name", "Stephen Curry");
    List<User> resultList = typedQuery.getResultList();

    for (User row : resultList) {
        System.out.println(row);
    }
}

 

예제) 함수 호출

더보기
@Test
@Transactional
void function_call() {
    CriteriaBuilder cb = entityManager.getCriteriaBuilder();
    CriteriaQuery<Tuple> query = cb.createQuery(Tuple.class);
    Root<User> user = query.from(User.class);

    query.multiselect(
        user.get("name").alias("name"),
        cb.lower(user.get("name")).alias("lowercase"),
        cb.length(user.get("name")).alias("length")
    );

    TypedQuery<Tuple> typedQuery = entityManager.createQuery(query);
    List<Tuple> resultList = typedQuery.getResultList();

    for (Tuple row : resultList) {
        System.out.println(row.get("name") + " " + row.get("lowercase") + " " + row.get("length"));
    }
}

 

예제) 동적 쿼리

더보기
@Test
@Transactional
void dynamic_query() {
    CriteriaBuilder cb = entityManager.getCriteriaBuilder();
    CriteriaQuery<User> query = cb.createQuery(User.class);
    Root<User> user = query.from(User.class);

    String searchName = "Stephen Curry";
    Integer minAge = null;
    List<Predicate> predicates = new ArrayList<>();

    if (searchName != null) predicates.add(cb.equal(user.get("name"), searchName));
    if (minAge != null) predicates.add(cb.greaterThanOrEqualTo(user.get("age"), minAge));

    query
        .select(user)
        .where(cb.and(predicates.toArray(new Predicate[0])));

    TypedQuery<User> typedQuery = entityManager.createQuery(query);
    List<User> resultList = typedQuery.getResultList();

    for (User row : resultList) {
        System.out.println(row);
    }
}