DEV ℧ Developer Diary

[JPA] JPQL - 프로젝션

해당 포스트는 인프런 김영한님의 자바 ORM 표준 JPA 프로그래밍 - 기본편 을 듣고 정리한 글입니다.

JPQL - 프로젝션

개요

  • SELECT 절에 조회할 대상을 지정하는 것
  • 프로젝션 대상: 엔티티, 임베디드 타입, 스칼라 타입(숫자, 문자등 기본 데이터 타입)
  • DISTINCT로 중복 제거

- 엔티티 프로젝션

  • SELECT m FROM Member m

JPQL은 createQuery로 영속성에 넣어주는지 확인해 보자.

/* 프로젝션 */
List<Member> members = entityManager.createQuery("SELECT m from Member m", Member.class)
        .getResultList();

Member findMember = members.get(0);
findMember.setAge(30);
/* update
    com.dhaudgkr.jpa07.jpql02.domain.Member */ update
        Member
    set
        age=?,
        TEAM_ID=?,
        username=?
    where
        id=?

업데이트 쿼리가 나가는것을 보아 영속성 컨텍스트에 Member를 담는 것을 알 수 있다.

  • SELECT m.team FROM Member m

Member에 있는 Team을 가져올때는 어떻게 가져와야 할까?

List<Team> teams = entityManager.createQuery("SELECT m.team from Member m", Team.class)
                    .getResultList();
/* SELECT
        m.team
    from
        Member m */ select
            team1_.id as id1_3_,
            team1_.age as age2_3_,
            team1_.username as username3_3_
        from
            Member member0_
        inner join
            Team team1_
                on member0_.TEAM_ID=team1_.id

이렇게 하면 자동으로 join이 되어 나가지만 권장하지는 않다. JPQL은 최대한 나가는 쿼리와 비슷하게 작성해야 한다.

임베디드 타입 프로젝션

  • SELECT m.address FROM Member m
entityManager.createQuery("SELECT o.address from Order o", Address.class)
                    .getResultList();
/* SELECT
        o.address
    from

    Order o */ select
        order0_.city as col_0_0_,
        order0_.street as col_0_1_,
        order0_.zipcode as col_0_2_ from
            ORDERS order0_

임베디드 타입은 무난히 가져오는 것을 볼 수 있다.

하지만 Address를 테이블로 지정해서 가져오는것은 할 수 없다.

  • 잘못된 예시
entityManager.createQuery("SELECT a from Address a", Address.class)
                    .getResultList();

스칼라 타입 프로젝션

  • SELECT m.username, m.age FROM Member m
entityManager.createQuery("SELECT m.username, m.age from Member m")
                    .getResultList();
/* SELECT
    m.username,
    m.age
from
    Member m */ select
        member0_.username as col_0_0_,
        member0_.age as col_1_0_
    from
        Member member0_

여러 값 조회

  • SELECT m.username, m.age FROM Member m
  1. Query 타입으로 조회
/* Object */
List objectList = entityManager.createQuery("SELECT m.username, m.age from Member m")
        .getResultList();

Object o = objectList.get(0);
Object[] objects = (Object[]) o;
System.out.println("objects.username : " + objects[0]);
System.out.println("objects.age : " + objects[1]);
/* SELECT
    m.username,
    m.age
from
    Member m */ select
        member0_.username as col_0_0_,
        member0_.age as col_1_0_
    from
        Member member0_

objects.username : member1
objects.age : 10
  1. Object[] 타입으로 조회
/* Object[] */
List<Object[]> results = entityManager.createQuery("SELECT m.username, m.age from Member m")
        .getResultList();

Object[] objects = results.get(0);
System.out.println("objects.username : " + objects[0]);
System.out.println("objects.age : " + objects[1]);
/* SELECT
    m.username,
    m.age
from
    Member m */ select
        member0_.username as col_0_0_,
        member0_.age as col_1_0_
    from
        Member member0_

objects.username : member1
objects.age : 10
  1. new 명령어로 조회
    • 단순 값을 DTO로 바로 조회 SELECT new jpabook.jpql.UserDTO(m.username, m.age) FROM Member m
    • 패키지 명을 포함한 전체 클래스 명 입력
    • 순서와 타입이 일치하는 생성자 필요
/* new */
List<MemberDto> result = entityManager.createQuery("SELECT new com.dhaudgkr.jpa07.jpql02.domain.MemberDto(m.username, m.age) from Member m", MemberDto.class)
                    .getResultList();
MemberDto memberDto = result.get(0);
System.out.println("MemberDto.username : " + memberDto.getUsername());
System.out.println("MemberDto.age : " + memberDto.getAge());
/* SELECT
    new com.dhaudgkr.jpa07.jpql02.domain.MemberDto(m.username,
    m.age)
from
    Member m */ select
        member0_.username as col_0_0_,
        member0_.age as col_1_0_
    from
        Member member0_

MemberDto.username : member1
MemberDto.age : 10