Criteria Criteria 위치Criteria 기본 사용법Criteria 쿼리 검색조건 추가하기Criteria 서브쿼리 사용하기커스텀 레포지토리 정의하는 방법Criteria를 이용한 동적쿼리 생성은 아직 삽질하고 있습니다..
Criteria
- Criteria 쿼리는 JPQL을 자바 코드로 작성하도록 도와주는 빌더 클래스 API다.
- Criteria를 사용하면 문자가 아닌 코드로 JPQL을 작성하므로 문법 오류를 컴파일 단계에서 잡을 수 있고 문자 기반의 JPQL보다 동적 쿼리를 안전하게 생성할 수 있다는 장점이 있다.
- 하지만 실제 Criteria를 사용해서 개발해보면 코드가 복잡하고 장황해서 직관적으로 이해하기 힘들다는 단점도 존재한다.
- 즉 Criteria는 JPQL의 생성을 돕는 클래스의 모음이다.
Criteria 위치
- Criteria API는 javax.persistence.criteria 패키지에 존재한다.
Criteria 기본 사용법

// JPQL : select m from Member m CriteriaBuilder cb = em.getCriteriaBuilder(); // 타입을 지정해주지 않으면 Object 타입이 나온다. CriteriaQuery<Member> cq = cb.createQuery(Member.class); Root<Member> m = cq.from(Member.class); // FROM 절 cq.select(m); // SELECT 절 TypedQuery<Member> query = em.createQuery(cq); List<Member> members = query.getResultList();
- Criteria 쿼리를 생성하려면 먼저 Criteria 빌더를 얻어야 한다. Criteria 빌더는 EntityManager나 EntityManagerFactory에서 얻을 수 있다.
- Criteria 쿼리 빌더에서 Criteria 쿼리를 생성한다. 이때 반환 타입을 지정할 수 있다.
- FROM 절을 생성한다. 반환된 값 m은 Criteria에서 사용하는 특별한 별칭이다. m을 조회의 시작점이라는 의미로 쿼리 루트라한다.
- SELECT 절을 생성한다.
- 이렇게 Criteria 쿼리를 완성하고 나면 다음 순서는 JPQL과 같다. em.createQuery(cq)에 완성된 Criteria 쿼리를 넣어주기만 하면 된다.
Criteria 쿼리 검색조건 추가하기
// JPQL // SELECT m FROM Member m // WHERE m.username = "김형욱" CriteriaBuilder cb = em.getCriteriaBuilder(); CriteriaQuery<Member> cq = cb.createQuery(Member.class); Root<Member> m = cq.from(Member.class); // FROM 절 생성 // 검색 조건 정의 Predicate usernameEqual = cb.equal(m.get("username"),"김형욱"); // 정렬 조건 정의 javax.persistence.criteria.Order ageDesc = cb.desc(m.get("age")); // 쿼리 생성하기 cq.select(m) .where(usernameEqual) .orderBy(ageDesc) List<Member> resultList = em.createQuery(cq).getResultList();
- 검색 조건을 정의한 부분을 보면 m.get(”username”) 으로 되어 있는데 m은 회원 엔티티의 별칭이다. 이것은 JPQL에서 m.username과 같은 표현이다. 그리고 cb.equal(A,B)는 이름 그대로 A = B 라는 뜻이다. 따라서 cb.equal(m.get(”username”), “김형욱")는 JPQL에서 m.username = “김형욱" 과 같은 표현이다.
- 정렬 조건을 정의하는 코드인 cb.desc(m.get(”age”))는 JPQL의 m.age desc와 같은 표현이다.
- 만들어준 조건을 where, orderBy 에 넣어서 원하는 쿼리를 생성한다.
- Criteria는 검색 조건부터 정렬까지 Criteria 빌더를 사용해서 코드를 완성한다.
추가적으로 IN, GroupBy 등 다양한 Query를 작성할 수 있다.
Criteria 서브쿼리 사용하기
/* SELECT m FROM Member m WHERE m.age >= (SELECT AVG(m2.age) from Member m2) */ CriteriaBuilder cb = em.getCriteriaBuilder(); CriteriaQuery<Member> mainQuery = cb.createQuery(Member.class); // 서브쿼리 생성하기 SubQuery<Double> subQuery = mainQuery.subQuery(Double.class); Root<Member> m2 = subQuery.from(Member.class); subQuery.select(m2.<Integer>get("age")); // 메인쿼리 만들기 Root<Member> m = mainQuery.from(Member.class) mainQuery.select(m) .where(cb.ge(m.<Integer>get("age"), subQuery));
- subQuery는 mainQuery를 이용해서 생성할 수 있다.
- 추라적으로 여기서 하나 알 수 있는 점은 m.get(”age”)에 <Integer>로 타입을 명시해 주었는데 가지고 올 때 “age”의 타입 정보를 알지 못한다. 그렇기 때문에 반환 타입 정보를 명시해주어야 한다. (String같은 문자열은 지정하지 않아도 된다.)
커스텀 레포지토리 정의하는 방법
- 커스텀 레포지토리 인터페이스를 아래의 예시처럼 만든다.
interface CustomizedPostRepository { List<Post> findAllSearch(); }
- 인터페이스의 구현체를 아래의 예시처럼 만들어준다
class CustomizedPostRepositoryImpl implements CustomizedPostRepository { public List<Post> findAllSearch() { ~~~ } }
- 사실 가장 중요한건 구현체 클래스 이름 뒤에
Impl
이라는 키워드이다. - 구현 자체는 Spring Data에 의존하지 않고 일반적인 Bean으로 관리된다. 즉 일반적인 의존성 주입을 통해(ex : jdbc Template) 주입하고 작업을 수행할 수 있다.
- 구현체를 적용하기 위해서는 아래의 예시처럼 인터페이스를 추가해주면 된다.
interface PostRepository extends JpaRepository<Post, Long>, CustomizedPostRepository { }
- 기본 레포지토리 인터페이스에 함께 확장하여 사용하면 기능이 결합되여 클라이언트가 사용할 수 있게 된다.
- 레포지토리 인터페이스에 인터페이스를 추가할 때마다 레포지토리 조각들을 추가하여 구성하게 된다.
- 기본 레포지토리 추가된 레포지토리 인터페이스는 스프링 데이터 모듈에서 제공하게 된다.
- 사용자 지정으로 구현된 레포지토리 기본 리포지토리보다 우선순위가 높다. 이 우선순위를 사용하면 동일한 메서드 시그니처를 제공하는 경우 모호한 부분을 해결할 수 있게 된다.
- 또한 레포지토리 하나의 기본 레포지토리에만 사용할 수 있는 것은 아니고 여러 레포지토리에서 사용할 수 있다.
interface CustomizedSave<T> { <S extends T> S save(S entity); } // MemberRepository, PostRepository 모두 사용 가능하다.
<repositories base-package="com.acme.repository" /> <repositories base-package="com.acme.repository" repository-impl-postfix="MyPostfix" />
- 네임스페이스 요소는 위의 repository-impl-postfix 속성을 지키면서 만들어야 하며 이 postfix의 기본값은 Impl이다. 직접 정의하고 싶은 경우는 위의 예시처럼 사용자가 직접 정의해서 설정할 수 있다.