- 참조: 자바 ORM 표준 JPA 프로그래밍
- Criteria 쿼리 생성
Criteria 를 사용하려면 CriteriaBuilder.createQuery() 메소드로 Criteria 쿼리를 생성하면 된다. CriteriaBuilder 인터페이스의 메소드 시그니처는 아래와 같다.
public interface CriteriaBuilder {
/**
* Create a <code>CriteriaQuery</code> object.
* @return criteria query object
*/
CriteriaQuery<Object> createQuery();
/**
* Create a <code>CriteriaQuery</code> object with the specified result
* type.
* @param resultClass type of the query result
* @return criteria query object
*/
<T> CriteriaQuery<T> createQuery(Class<T> resultClass);
/**
* Create a <code>CriteriaQuery</code> object that returns a tuple of
* objects as its result.
* @return criteria query object
*/
CriteriaQuery<Tuple> createTupleQuery();
위의 3 개의 메소드가 구성요소의 전부는 아니다. 메소드 시그니처를 살펴보면 파라미터를 이용하여 쿼리 결과의 반환 타입을 지정할 수 있다.
//Criteria query builder
CriteriaBuilder cb = em.getCriteriaBuilder();
//Criteria 생성, 반환 타입 지정
CriteriaQuery<Member> cq = cb.createQuery(Member.class);
Root<Member> roots = cq.from(Member.class);
cq.select(roots);
List<Member> members = em.createQuery(cq)
.getResultList();
CriteriaBuilder 와 엔티티 매니저 둘다 메소드명이 createQuery 인데 헷갈리므로 의미를 잘 생각하면서 코드를 읽어야 한다. CriteriaBuilder 의 createQuery 는 CriteriaQuery 를 반환한다. 하지만 엔티티 매니저의 createQuery 는 TypedQuery 를 반환한다. 위 예제에서는 체이닝 메소드 방식으로 곧바로 getResultList() 를 호출하여 더 헷갈리는 면도 있다.
CriteriaBuilder 의 createQuery 를 이용하여 CriteriaQuery 생성시 Member.class 를 반환 타입으로 지정하면, 엔티티 매니저의 createQuery 메소드 시그니처는 아래와 같기 때문에 반환형을 지정하지 않아도 된다.
public <T> TypedQuery<T> createQuery(CriteriaQuery<T> criteriaQuery);
- Object 형 반환
만약 반환타입을 지정할 수 없거나 둘 이상이면 Object 형으로 반환받으면 된다.
//Criteria query builder
CriteriaBuilder cb = em.getCriteriaBuilder();
//Criteria 생성, 반환 타입 지정
CriteriaQuery<Object> cq = cb.createQuery();
Root<Member> roots = cq.from(Member.class);
cq.select(roots);
List<Object> members = em.createQuery(cq)
.getResultList();
cq.from 의 인자에 Object.class 를 넘기면 안된다. 반환타입인 select 를 Object 형으로 지정하는것이지 from 절을 Object 로 정하는것이 아니다. JPQL 의 from 절은 엔티티여야 한다.
그리고 어차피 from 의 인자를 Object 로 지정할 수 도 없다. 아래와 같이 하이버네이트에서 validation 을 하기 때문에 엔티티형을 인자로 넘겨야 한다.
public <X> Root<X> from(Class<X> entityClass) {
EntityType<X> entityType = criteriaBuilder.getEntityManagerFactory()
.getMetamodel()
.entity( entityClass );
if ( entityType == null ) {
throw new IllegalArgumentException( entityClass + " is not an entity" );
}
return from( entityType );
}
- Object[] 형 반환
만약 반환 타입이 둘 이상일때는 어떻게 해야할까? 센스가 좀 있는 편이라면 앞에서 JPQL 프로젝션에서 Object[] 형으로 지정한것이 떠오를것이다.
//Criteria query builder
CriteriaBuilder cb = em.getCriteriaBuilder();
//Criteria 생성, 반환 타입 지정
CriteriaQuery<Object[]> cq = cb.createQuery(Object[].class);
Root<Member> roots = cq.from(Member.class);
roots.join("orders");
cq.multiselect(roots);
List<Object[]> members = em.createQuery(cq)
.getResultList();
아직 criteria join 을 배우지는 않았지만 multiselect 를 사용하여 Object[] 반환타입을 보여주기 위해 사용하였다. join 을 하지 않으면 Member 타입을 Object[] 형으로 캐스팅할 수 없기 때문에 에러가 발생한다.
- Tuple 형 반환
마지막으로 반환 타입 중 Tuple 에 대해 알아보도록 하자. Mybatis 에서 Query 결과 맵핑을 할 때 객체가 아니라 Map 을 사용하는 프로젝트도 있다.
물론 Map 을 사용하면 내부에 어떤값이 들어있을지도 모르고 Type 도 불분명한 점도 있는 등 단점이 있다. 그래서 소위 최신기술 스택을 열심히 공부하는 힙한 개발자들이 Map 사용 방식을 대차게 까기도 한다. 물론 나도 단점이 많다고 생각한다.
하지만 프로젝트에서 나머지 팀원들이 자바 객체를 만들기가 귀찮아서 Map 을 표준으로 잡아 달라고 바락바락 우기면 답이 없다. 우리는 학교과제가 아니라 돈을 받고 기한내에 완성을 해주는 프로이기 때문에 팀원을 설득할 자신이 없거나 팀원이 무조건 Map 을 사용하자고 하면 기꺼이 그렇게 해야 한다.
또한 필드에서 절대적으로 좋거나 나쁜것은 없다. 자본대비 아웃풋도 고려할 줄 알아야 한다. 사용할 일이 적더라도 숙지는 하고 있도록 하자.
//Criteria query builder
CriteriaBuilder cb = em.getCriteriaBuilder();
//Criteria 생성, 반환 타입 지정
CriteriaQuery<Tuple> cq = cb.createTupleQuery();
Root<Member> roots = cq.from(Member.class);
cq.multiselect(
roots.get("name").alias("userName"),
roots.get("id").alias("userId"));
List<Tuple> members = em.createQuery(cq)
.getResultList();
members.forEach(member -> {
System.out.println("member's name: " + member.get("userName"));
});
위의 코드를 실행하면 아래와 같은 SQL 이 생성된다.
Hibernate:
/* select
generatedAlias0.name,
generatedAlias0.id
from
Member as generatedAlias0 */ select
member0_.name as col_0_0_,
member0_.member_id as col_1_0_
from
member member0_
multiselect 에서 alias 를 준다고 해서 실제 SQL 의 alias 로 지정되는것은 아님을 알 수 있다.
사용법은 그렇게 어렵지는 않다. 위의 코드를 읽어보면 alias 로 지정한 명을 Map 처럼 꺼내쓰기만 하면 된다. 튜플의 자세한 사용법은 이후에 알아본다.
'Framework and Tool > JPA' 카테고리의 다른 글
JPA - 객체지향 쿼리 언어 - Criteria 집합, 정렬, 조인 (0) | 2021.08.09 |
---|---|
JPA - 객체지향 쿼리 언어 - Criteria 조회 (0) | 2021.08.08 |
JPA - 객체지향 쿼리 언어 - Criteria (0) | 2021.08.04 |
JPA - 객체지향 쿼리 언어 - JPQL NamedQuery (0) | 2021.08.03 |
JPA - 객체지향 쿼리 언어 - JPQL 다형성 쿼리 (0) | 2021.08.03 |
댓글