- 참조: 자바 ORM 표준 JPA 프로그래밍
- 조회(select & multiselect)
Criteria 에서 SELECT 절을 만드는 함수는 아래와 같다.
/**
* The <code>CriteriaQuery</code> interface defines functionality that is specific
* to top-level queries.
*
* @param <T> the type of the defined result
*
* @since 2.0
*/
public interface CriteriaQuery<T> extends AbstractQuery<T> {
CriteriaQuery<T> select(Selection<? extends T> selection);
CriteriaQuery<T> multiselect(Selection<?>... selections);
CriteriaQuery<T> multiselect(List<Selection<?>> selectionList);
...........
조회 대상을 한 건을 지정할 것이냐 혹은 여러 건을 지정할 것이냐에 따라 select 혹은 multiselect 를 사용하면 된다.
//Criteria query builder
CriteriaBuilder cb = em.getCriteriaBuilder();
//Criteria 생성, 반환 타입 지정
CriteriaQuery<Member> cq = cb.createQuery(Member.class);
Root<Member> m = cq.from(Member.class);
cq.select(m);
List<Member> members = em.createQuery(cq)
.getResultList();
members.forEach(member -> {
System.out.println("member's name: " + member.getName());
});
위의 예제는 JPQL "select m from Member m" 에서 Member 엔티티 한 건을 조회하는 select 예제이다. 만약 "select m.name, m.id from Member m" 과 같이 select 절에서 여러 건을 조회하고 싶다면 아래와 같이 multiselect 를 이용해야 한다.
//Criteria query builder
CriteriaBuilder cb = em.getCriteriaBuilder();
//Criteria 생성, 반환 타입 지정
CriteriaQuery<Object[]> cq = cb.createQuery(Object[].class);
Root<Member> m = cq.from(Member.class);
cq.multiselect(m.get("name"), m.get("id"));
List<Object[]> members = em.createQuery(cq)
.getResultList();
members.forEach(member -> {
System.out.println("member's name: " + member[0]);
System.out.println("member's id: " + member[1]);
});
여러 건에 대해 다른 맵핑방법은 아직 배우기 전이므로 CriteriaQuery 의 반환형을 Object[] 로 지정해주었다. cq.from 는 앞에서 언급했듯이 JPQL 에서 from 절에 해당하므로 from Member 와 같이 엔티티형이어야 한다. multiselect 함수 에서는 조회하려고 하는 대상들을 지정한다.
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_
member's name: Name#1
member's id: 1
member's name: Name#2
member's id: 2
member's name: Name#3
member's id: 3
member's name: Name#4
member's id: 4
수행된 SQL 과 출력결과는 위와 같다. select 절에 조회대상이 잘 기술되었다.
- DISTINCT
Criteria 는 JPQL 과 마찬가지로 DISTINCT 기능도 제공한다. select 후 distinct(true)를 추가해주면 JPQL 의 DISTINCT 기능을 이용할 수 있다. DISTINCT 기능 테스트를 위해 member2, 3, 4 의 이름을 Name#2 로 변경해주자.
Member member1 = new Member();
member1.setName("Name#1");
em.persist(member1);
Member member2 = new Member();
member2.setName("Name#2");
em.persist(member2);
Member member3 = new Member();
member3.setName("Name#2");
em.persist(member3);
Member member4 = new Member();
member4.setName("Name#2");
em.persist(member4);
.....................
//Criteria query builder
CriteriaBuilder cb = em.getCriteriaBuilder();
//Criteria 생성, 반환 타입 지정
CriteriaQuery<Object> cq = cb.createQuery();
Root<Member> m = cq.from(Member.class);
cq.select(m.get("name")).distinct(true);
List<Object> members = em.createQuery(cq)
.getResultList();
members.forEach(member -> {
System.out.println("member's name: " + member);
});
criteria 부분 코드에서 select 뒤에 distinct(true) 가 추가된것을 볼 수 있다. 위 예제를 실행하면 SQL 이 아래처럼 변환되어 수행된다.
Hibernate:
/* select
distinct generatedAlias0.name
from
Member as generatedAlias0 */ select
distinct member0_.name as col_0_0_
from
member member0_
member's name: Name#1
member's name: Name#2
distinct(true) 를 false 로 변환하여 수행하면 Name#2 가 중복되어 나오는 모습을 확인할 수 있다.
- 프로젝션(NEW, construct)
JPQL 프로젝션에서 new 구문을 배웠었다. Object[] 로 반환형을 결과로 참조한 다음 또 다시 형변환을 해주는 번거로움을 없애고자 DTO 를 하나 추가하여 new 뒤에 패키지명을 포함한 생성자구문으로 이를 해결했었다. 기억을 되뇌일 겸 코드를 다시한번 살펴보자.
public class MemberDTO {
private Long id;
private String name;
..............
String projectionJpql = "select new com.example.demo.member.dto.MemberDTO(m.id, m.name) "
+ "from Member m";
List<MemberDTO> membersInfo = em.createQuery(projectionJpql, MemberDTO.class)
.getResultList();
membersInfo.forEach(System.out::println);
...........
Hibernate:
/* select
new com.example.demo.member.dto.MemberDTO(m.id,
m.name)
from
Member m */ select
member0_.member_id as col_0_0_,
member0_.name as col_1_0_
from
member member0_
com.example.demo.member.dto.MemberDTO@2932e15f
com.example.demo.member.dto.MemberDTO@3d98729a
com.example.demo.member.dto.MemberDTO@2375a976
com.example.demo.member.dto.MemberDTO@4bc21e34
형변환 필요없이 깔끔하게 DTO 계층을 이용하여 Member 엔티티의 일부 정보를 다룰 수 있다. 참고로 DTO 계층이라는 말에 대해 더 깊게 이해를 하고 싶다면 로버트 C.마틴의 클린아키텍처나 에릭 에반스의 DDD 에 해당 부분을 참조하면 된다. Criteria 를 이용하여 위의 JPQL 예제와 같이 반환형을 DTO 로 다루어보자.
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<MemberDTO> cq = cb.createQuery(MemberDTO.class);
Root<Member> m = cq.from(Member.class);
cq.select(cb.construct(MemberDTO.class,
m.get("id"), m.get("name")));
List<MemberDTO> members = em.createQuery(cq)
.getResultList();
members.forEach(member -> {
System.out.println("member's name: " + member.getName());
});
cq.select 의 인자가 주의깊게 살펴보자. CriteriaBuilder 의 constrcut 를 이용하여 프로젝션을 수행한다. 우선 반환형 클래스 타입을 주어야 한다. JPQL 은 String 기반이라 패키지명을 모두 지정해줘야했지만 criteria 는 코드 기반이므로 MemberDTO.class 와 같은 방식으로 작성하는게 가능해진다. 그리고 이어서 생성자 인자들을 지정해준다.
수행되는 결과는 JPQL 로 프로젝션 쿼리를 작성할때와 같다.
Hibernate:
/* select
new com.example.demo.member.dto.MemberDTO(generatedAlias0.id,
generatedAlias0.name)
from
Member as generatedAlias0 */ select
member0_.member_id as col_0_0_,
member0_.name as col_1_0_
from
member member0_
member's name: Name#1
member's name: Name#2
member's name: Name#3
member's name: Name#4
- 튜플
Criteria 도 Map 과 같은 비슷한 튜플이라는 기능을 지원한다.
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Tuple> cq = cb.createTupleQuery();
Root<Member> m = cq.from(Member.class);
cq.multiselect(m.get("id").alias("memberId"),
m.get("name").alias("memberName"));
List<Tuple> members = em.createQuery(cq)
.getResultList();
members.forEach(member -> {
System.out.println("member's name: " + member.get("memberName"));
});
CriteriaQuery 에서 반환형을 Tuple 로 지정한다. 그리고 multiselect 에서 튜플 별칭들을 지정해준다. Tuple 을 사용할 때에는 Map 처럼 alias 에서 지정한 문자열로 정보를 불러오면 된다.
Hibernate:
/* select
generatedAlias0.id,
generatedAlias0.name
from
Member as generatedAlias0 */ select
member0_.member_id as col_0_0_,
member0_.name as col_1_0_
from
member member0_
member's name: Name#1
member's name: Name#2
member's name: Name#3
member's name: Name#4
당연히 튜플도 JPQL 의 select m from Member m 처럼 엔티티를 조회할 수 있다.
cq.multiselect(
m.alias("m"),
m.get("id").alias("memberId"),
m.get("name").alias("memberName"));
Hibernate:
/* select
m,
m.id,
m.name
from
Member as m */ select
member0_.member_id as col_0_0_,
member0_.member_id as col_1_0_,
member0_.name as col_2_0_,
member0_.member_id as member_i1_6_,
member0_.insert_datetime as insert_d2_6_,
member0_.update_datetime as update_d3_6_,
member0_.city as city4_6_,
member0_.street as street5_6_,
member0_.zipcode as zipcode6_6_,
member0_.name as name7_6_
from
member member0_
member's name: Name#1
member's name: Name#2
member's name: Name#3
member's name: Name#4
수행된 SQL 을 잘 보면 name 과 id 가 2번씩 select 절에 나타났고, Member 엔티티의 모든 속성이 나타난다.
'Framework and Tool > JPA' 카테고리의 다른 글
JPA - 객체지향 쿼리 언어 - Criteria 서브 쿼리 (0) | 2021.08.11 |
---|---|
JPA - 객체지향 쿼리 언어 - Criteria 집합, 정렬, 조인 (0) | 2021.08.09 |
JPA - 객체지향 쿼리 언어 - Criteria 쿼리 생성 (0) | 2021.08.05 |
JPA - 객체지향 쿼리 언어 - Criteria (0) | 2021.08.04 |
JPA - 객체지향 쿼리 언어 - JPQL NamedQuery (0) | 2021.08.03 |
댓글