본문 바로가기
자바/JPA

[JPA/Java] JPQL Projection(프로젝션)

by drCode 2023. 4. 28.
728x90
반응형

JPQL 프로젝션은 SELECT 절에 조회할 대상을 지정하는 것을 의미한다.

 

프로젝션 대상에는 엔티티, 임베디드 타입, 스칼라 타입(숫자, 문자 등 기본 데이터 타입) 이 해당된다.

 

엔티티 프로젝션에는 쿼리로 표현하면

SELECT m FROM Member m

SELECT m.team FROM Member m

이 있고,

 

이를 코드로 표현하면

List<Member> result = em.createQuery("select m from Member m", Member.class).getResultList();

List<Team> result = em.createQuery("select m.team from Member m join m.team t", Team.class).getResultList();

 

와 같이 표현할 수 있다.

 

임베디드 타입 프로젝션은 쿼리로 표현하면

SELECT m.address FROM Member m

이 있고,

 

이를 코드로 표현하면

List<Address> addresses = em.createQuery("select o.address from Order o", Address.class).getResultList();

 

와 같이 된다.

 

스칼라 타입 프로젝션은 쿼리롤 표현하면

SELECT m.username, m.age FROM Member m

처럼 표현할 수 있고,

 

이를 코드로 표현하면

List resultList = em.createQuery("select distinct m.username, m.age from Member m").getResultList();
Object o = resultList.get(0);
Object[] result = (Object[]) o;
System.out.println("username = " + result[0]);
System.out.println("age = " + result[1]);

 

List<Object[]> resultList = em.createQuery("select distinct m.username, m.age from Member m")
                              .getResultList();
Object[] result = resultList.get(0);
System.out.println("username = " + result[0]);
System.out.println("age = " + result[1]);

와 같이 쓸 수 있다.

 

위처럼 "DISTINCT" 를 붙이면 중복을 없앨 수 있다.

 

※ 프로젝션으로 여러 값을 조회할 땐

SELECT m.username, m.age FROM Member m

를 예로 들면

 

(1) Query 타입으로 조회할 수 있다. → 반환 타입이 명확하지 않을 때 이처럼 사용할 수 있다.

Query query = em.createQuery("SELECT m.username, m.age from Member m");

 

(2) Object[] 타입으로 조회

List<Object[]> resultList = em.createQuery("select distinct m.username, m.age from Member m")
                              .getResultList();
Object[] result = resultList.get(0);
System.out.println("username = " + result[0]);
System.out.println("age = " + result[1]);

 

(3) new 명령어로 조회 → 이 방법을 가장 추천, 제일 깔끔한 방법이다.

 - 단순 값을 DTO로 바로 조회할 수 있다.

SELECT new jpabook.jpql.UserDTO(m.username, m.age) FROM Member m

 - 패키지 명을 포함한 전체 클래스 명을 입력한다

 - 순서와 타입이 일치하는 생성자가 필요하다.

 

MemberDTO.java

package jpql;

public class MemberDTO {

    private String username;
    private int age;

    public MemberDTO(String username, int age) {
        this.username = username;
        this.age = age;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

 

JpaMain.java

package jpql;

import javax.persistence.*;
import java.util.List;

public class JpaMain {

    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("member1");
            member.setAge(10);
            em.persist(member);
            
            List<MemberDTO> resultList = em.createQuery("select new jpql.MemberDTO(m.username, m.age) from Member m", MemberDTO.class).getResultList();

            MemberDTO memberDTO = resultList.get(0);
            System.out.println("memberDTO.getUsername() = " + memberDTO.getUsername());
            System.out.println("memberDTO.getAge() = " + memberDTO.getAge());

            tx.commit();
        } catch (Exception e) {
            tx.rollback();
            e.printStackTrace();
        } finally {
            em.close();
        }

        // code
        emf.close();
    }
}

 

728x90
반응형

댓글