1. 프로젝션이란?
- SELECT 절에 조회할 대상을 지정하는 것
2. 프로젝션 대상
✔️엔티티, 임베디드 타입, 스칼라 타입(기본 데이터 타입)
관계형 데이터베이스는 테이블 내의 개별적인 값을 가지는 데이터들을 조회한다.
예를 들어, Employees 테이블에 EmployeeID, Name, Age와 같은 속성에 있는 값을 조회하는 거다.
프로젝션 대상이 스칼라 타입이라고 볼 수 있다.
JPQL에서는 관계형 데이터베이스와 달리 조회 대상이 더 넓다.
3. 프로젝션 대상 이해하기
✅ Member 엔티티
@Entity
@Getter
@Setter
public class Member {
@Id @GeneratedValue
private Long id;
private String username;
private int age;
@Embedded
private Address address;
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
@OneToMany(mappedBy = "member")
private List<Order> orders = new ArrayList<>();
}
Member 엔티티의 조회 대상들을 타입 별로 구분해보면,
- 엔티티 타입: team
- 임베디드 타입 : address
- 스칼라 타입 : id, username, age
✅JPQL 조회 예시
SELECT m FROM Member m -- 엔티티 프로젝션
SELECT m.team FROM Member m -- 엔티티 프로젝션
SELECT m.address FROM Member m -- 임베디드 프로젝션
SELECT m.username, m.age FROM Member m -- 스칼라 타입 프로젝션
✅실행 메서드
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try{
//멤버 엔티티 저장
Member member = new Member();
member.setUsername("memberA");
member.setAge(10);
em.persist(member);
em.flush();
em.clear();
//Member 조회
List<Member> result = em.createQuery("select m from Member m", Member.class)
.getResultList();
Member findMember = result.get(0);
findMember.setAge(20); //update 쿼리실행
//Team 조회
List<Team> result2 = em.createQuery("select t from Member m join m.team t", Team.class)
.getResultList();
//Address 조회
List<Address> result3 = em.createQuery("select m.address from Member m", Address.class)
.getResultList();
//여러 타입 조회 - Query 객체로 쿼리 실행하고 결과를 Object[]로 받기
Query query = em.createQuery("select m.username, m.age from Member m");
List<Object[]> result4 = query.getResultList();
Object[] selectResult4 = result4.get(0);
System.out.println("username = " + selectResult4[0]);
System.out.println("age = " + selectResult4[1]);
//여러 타입 조회 - new 명령어로 조회(new뒤에 패키지명)
List<MemberDTO> result5 = em.createQuery("select new jpql.MemberDTO(m.username, m.age) from Member m", MemberDTO.class)
.getResultList();
MemberDTO memberDTO = result5.get(0);
System.out.println("username = " + memberDTO.getUsername());
System.out.println("age = " + memberDTO.getAge());
tx.commit();
}catch (Exception e){
tx.rollback();
e.printStackTrace(); //예외 출력
}finally {
em.close();
}
}
- Member를 조회하기 전에 em.clear()로 영속성 컨텍스트를 초기화 해주지만 Member 엔티티를 조회하며 영속성 컨텍스트에 다시 로드한다. 그래서 setAge()를 호출했을 때 update 쿼리가 실행되는 것이다.
- select m.team from Member m 로 작성하면 team을 조회할 때 조인쿼리가 실행된다.
select t from Member m join Team t 처럼 join이 발생한다는 것을 코드만 봐도 이해할 수 있도록 명시해주는 게 좋다.
- Address와 같은 임베디드 타입은 임베디드 타입이 속해있는 엔티티를 거쳐서 조회할 수 있다.
select a from Address a 이런 쿼리로 조회할 수 없다는 말이다.
4. 여러가지 타입을 조회하는 방법
- username은 String 타입이고 age는 int 타입이기때문에 반환되는 타입이 명확하지 않다.
여러가지 타입을 한번에 조회하는 두가지 방법이 있다.
4-1) Object[] 타입으로 조회
Query 객체로 JPQL 쿼리를 실행하고 결과를 Object[] 타입으로 받아서 조회하는 방법.
(메서드 체이닝으로 처음부터 Object[] 타입으로 받을 수 있으나 Query 복습겸 넣음)
쿼리에서 조회 대상의 순서를 어떻게 작성했는지 알아야하기 때문에 코드가 깔끔하지 않다.
4-2) new 명령어로 조회
먼저 DTO 클래스가 필요하다.
@Getter
@Setter
public class MemberDTO {
private String username;
private int age;
public MemberDTO(String username, int age) {
this.username = username;
this.age = age;
}
}
- new 문법은 결과를 엔티티가 아닌 특정 DTO로 매핑하기 위해 사용된다.
- JPQL 쿼리를 실행하면 생성자를 호출해서 MemberDTO 인스턴스가 생성된다.
- DTO에 생성자를 필수로 만들어줘야한다.
- Object[]타입과 달리, new 명령어로 조회를 하면 getter를 사용할 수 있기 때문에 코드가 좀 더 명확해진다.
'인프런 김영한 강의 정리 > 자바 ORM 표준 JPA 프로그래밍 기본편' 카테고리의 다른 글
JPA 기본 | JPQL 문법(4) - ENUM 타입,상속관계 엔티티 표현 (0) | 2024.07.17 |
---|---|
JPA 기본 | JPQL 문법(3) - 페이징, 조인, 서브쿼리 (0) | 2024.07.17 |
JPA 기본 | JPQL 기본 문법(1) 쿼리 API, 파라미터 바인딩 (0) | 2024.07.15 |
JPA 기본 | 객체지향 쿼리 언어 알아보기(JPQL, QueryDSL 등) (0) | 2024.07.15 |
JPA 기본 | 데이터 타입(3) - 값 타입 컬렉션 (0) | 2024.07.14 |