0. 개요
- 이번 포스팅에서는 JPQL을 이용하여 Select 절을 작성하는 방법에 대해서 알아보자.
1. 프로젝션(= projection)
- 프로젝션이란 JPQL의 Select 절을 이용하여 어떤 데이터를 조회할 것인가를 말한다.
- Select를 절의 조회 대상을 무엇으로 작성하냐에 따라 프로젝션의 종류가 달라진다.
- 프로젝션은 조회하는 데이터의 속성에 따라서 다음 3가지 종류로 구분된다.
→ Entity(객체) 프로젝션
→ Embedded 프로젝션
→ 스칼라 프로젝션
2. Entity 프로젝션
a) Entity 프로젝션이란?
- 객체 또는 객체의 속성을 조회하는 Select문을 말한다.
b) 기본 사용방법
- 다음 예시를 보면서 이해해보자.
- 첫 번째 예시는 객체를 조회하는 프로젝션이다.
- Entity 프로젝션은 조회하는 객체의 클래스가 매개변수로 들어간다.(= Typed Query)
em.createQuery("select p from Player p", Player.class);
- 두 번째 예시는 객체가 가진 속성을 조회하는 프로젝션이다.
- 여기서 주의해야 할 것은 team은 값 타입이 아니라 Entity라는 것이다.
- 다만, Team은 Entity로 존재하기 때문에 Team 클래스가 매개변수로 들어간다.
em.createQuery("select p.team from Player p", Team.class);
3. Embedded 타입 프로젝션
a) Embedded 타입 프로젝션이란?
- Entity의 속성 중 값 타입으로 생성된 속성을 조회하는 Select문을 말한다.
b) 기본 사용 방법
- Embedded 프로젝션의 사용 방법은 Entity 프로젝션을 사용하는 방법과 동일하다.
- 다음 예시를 살펴보자.
-조회하는 데이터가 값 타입이므로 값 타입 클래스가 매개변수로 들어간다.(= Typed Query)
em.createQuery("select p.address from Player p", Address.class);
4. 스칼라 타입 프로젝션
a) 스칼라 타입 프로젝션이란?
- 스칼라 타입 프로젝션은 조회하는 데이터의 타입과 상관없이 여러 개를 가져오는 방법이다.
b) 기본 사용 방법
- 다음 예시를 살펴보자.
em.createQuery("select p.name, p.age, p.team from Player p");
- 위의 코드에서 조회하는 데이터의 속성은 모두 다르다.
→ name은 String, age는 int, team은 Entity
- 그러므로 createQuery() 메서드에 조회하는 데이터의 클래스가 매개변수로 들어가지 않는다. (= Query)
- 스칼라 타입 프로젝션은 조회되는 데이터의 자료형을 규정할 수 없으므로 Query 형식을 사용한다.
c) Query
- 이전 포스팅에서 TypedQuery와 Query에 대해서 알아보았다.
- 스칼라 타입 프로젝션은 서로 다른 자료형을 가진 데이터를 조회하므로, Query를 사용한다.
- Query 형식은 조회 결과의 자료형을 규정할 수 없으므로 Object 배열을 사용하여 받아야 한다.
- 다음 예시 코드를 살펴보자.
List<Object[]> resultList =
em.createQuery("select p.name, p.address, p.team from Player p")
.getResultList();
Object[] result = resultList.get(0);
System.out.println("result = " + result[0]);
System.out.println("result = " + result[1]);
System.out.println("result = " + result[2]);
- 다음 사진은 실행 결과다.
- 위의 코드처럼, 스칼라 타입 프로젝션의 경우 Object 배열을 사용하여 데이터를 받아온다.
- 그러나 이 방식은 선호되는 방식은 아니다. 그러므로 DTO와 new 키워드를 사용한 방법을 알아보자.
d) DTO와 new 키워드를 사용한 방법
- 위에서 Object 배열을 사용한 방법은 코드가 깔끔하지 않다.
- 더불어, 데이터를 추출하는 과정도 깔끔하다고 할 수 없다.
- 그러므로 DTO와 new 키워드를 사용하여 데이터를 받아올 수 있다.
- DTO는 스칼라 타입 프로젝션의 조회 결과를 담는, 일종의 자료형(= 클래스)처럼 작동하는 것이다.
- 예를 들어, 위에서 사용한 예시처럼 name(= String), address(= 값 타입), team(= Entity)을 조회하려고 한다.
- 그렇다면 다음과 같이 DTO를 형성할 수 있다.
public class PlayerDTO {
private String name;
private Address address;
private Team team;
public PlayerDTO(String name, Address address, Team team) {
this.name = name;
this.address = address;
this.team = team;
}
// Getter & Setter 생략
}
- 그다음에는 new jpql.DTO를 사용하여 다음과 같이 JPQL을 작성한다.
List<PlayerDTO> resultList =
em.createQuery("select new jpql.PlayerDTO(p.name, p.address, p.team) from Player p", PlayerDTO.class)
.getResultList();
PlayerDTO playerDTO = resultList.get(0);
System.out.println("playerDTO.getName() = " + playerDTO.getName());
System.out.println("playerDTO.getAddress() = " + playerDTO.getAddress());
System.out.println("playerDTO.getTeam() = " + playerDTO.getTeam());
- 이처럼 DTO와 new 연산자를 사용하여 데이터를 받아올 수 있다.
- 그러나 매번 데이터를 받아오기 위해서 DTO를 작성할 수는 없다.
- 그러므로 해당 데이터를 자주 조회하는 경우에는 DTO를 이용한 방식을 사용한다.
5. 프로젝션 사용 시 주의사항
a) 조회 결과는 영속성 Context에 의해 관리된다.
- 프로젝션으로 조회된 결과는 영속성 Context에 의해 관리된다.
- 다음 예시를 보면서 이해해보자.
- DB에 다음과 같은 데이터가 저장되어 있다.
- 그리고 다음 코드를 실행하면 어떻게 될지 생각해보자.
List<Player> result = em.createQuery("select p from Player p", Player.class)
.getResultList();
Player findPlayer = result.get(0);
findPlayer.setName("Messi");
findPlayer.setPosition("CAM");
- 위의 예시를 보면 Entity 프로젝션으로 가져온 값 중 하나를 setName() 메서드를 이용하여 값을 변경하였다.
- 위의 코드를 실행하면 해당 객체의 name 속성은 "Messi"로, position 속성은 "CAM"으로 변경된다.
- 이처럼 조회된 값의 update가 가능한 이유는, 조회된 결과가 영속성 Context에 의해 관리되기 때문이다.
- 코드의 실행 결과를 DB에서 확인해보자.
b) Join을 사용할 때에는 반드시 Join을 명시하자.
- 다음 예시 코드를 살펴보자.
List<Team> resultList = em.createQuery("select p.team from Player p", Team.class)
.getResultList();
- 위의 코드를 실행하면 다음과 같은 쿼리문이 발생한다.
- 이처럼 inner join이 발생한 것을 확인할 수 있다.
- 어떻게 생각해보면 굉장히 편한 기능이라고 생각할 수 있다.
- 개발자가 굳이 join을 명시하지 않아도 JPA가 알아서 join을 사용하여 데이터를 조회하기 때문이다.
- 그러나 유지보수의 측면에서 볼 때, JPQL만 읽고 JPA가 어떤 쿼리를 실행하는지 정확히 알 수 없다.
- 그러므로 Join이 사용되면 반드시 JPQL에도 직접 Join을 명시하는 것을 권장한다.
- 이 규칙을 따르면 다음과 같이 JPQL을 작성할 수 있다.
List<Team> resultList =
em.createQuery("select t from Player p join p.team t", Team.class)
.getResultList();
- 새로 작성된 JPQL을 실행하더라도 동일한 쿼리문이 발행된다.
- 다만, JPA가 실행할 쿼리를 정확히 명시하여 개발자의 이해를 돕는다는 것에 의미와 목적이 있다.
'Back-end > JPA 개념' 카테고리의 다른 글
26. JPQL - 조인 (0) | 2022.04.26 |
---|---|
25. JPQL - 페이징 (0) | 2022.04.25 |
23. JPQL - 기본 기능(파라미터 바인딩, 결과 조회, 쿼리의 종류) (0) | 2022.04.21 |
22. JPQL - JPQL에 대하여 (0) | 2022.04.20 |
21. 값 타입(4) - 값 타입과 컬렉션 (0) | 2022.04.19 |
댓글