본문 바로가기

Spring/JPA

JPA- 프로젝션

프로젝션

 프로젝션이란 select 절에 조회할 대상을 쿼리를 통해서 지정하는 것을 의미한다. 만일 엔티티 Member에 name, age라는 필드가 존재하고 그 중 내가 name을 조회하고 싶다라면 조회할 대상으로 name을 지정하는 것을 의미한다.

 jpql을 통해서 값 가져오기

다양한 값을 조회해 가져오기

예시) 연관 관계 엔티티 프로젝션

package jpql;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import java.util.List;

public class JpqlMain {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();
        EntityTransaction tx = em.getTransaction();
        tx.begin();

        try {
            Team team = new Team();
            team.setName("TEAM1");

            Member member = new Member();
            member.setUsername("member1");
            member.setAge(10);
            member.changeTeam(team);

            em.persist(member);

            em.flush();
            em.clear();


            List<Team> resultList =
                    em.createQuery("select m.team from Member m", Team.class).getResultList();
                    
            /*List<Team> resultList =
                    em.createQuery("select t from Team t", Team.class).getResultList();*/
                    
            Team team1 = resultList.get(0);
            System.out.println("team1 = " + team1.getName());

            tx.commit();
        } catch (Exception e) {
            tx.rollback(); // 오류 발생 시 롤백
            e.printStackTrace(); // 에러 내용 출력
        } finally {
            //종료
            em.close();
        }
        emf.close();
    }
}
Hibernate: 
    /* insert jpql.Team
        */ insert 
        into
            Team
            (name, id) 
        values
            (?, ?)
Hibernate: 
    /* insert jpql.Member
        */ insert 
        into
            Member
            (age, TEAM_ID, username, id) 
        values
            (?, ?, ?, ?)
Hibernate: 
    /* select
        m.team 
    from
        Member m */ select
            team1_.id as id1_3_,
            team1_.name as name2_3_ 
        from
            Member member0_ 
        inner join
            Team team1_ 
                on member0_.TEAM_ID=team1_.id
team1 = TEAM1

여러 값 조회

 Object로 조회하는 경우

예시) 스칼라 타입 프로젝션

package jpql;

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

public class JpqlMain {
    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);

            em.flush();
            em.clear();
			
            // 여러 개의 스칼라 타입 프로젝션(username, age)
            List resultList = em.createQuery("select m.username, m.age from Member m")
                    .getResultList();
            Object o = resultList.get(0);

            // 배열형으로 형 변환
            Object[] results = (Object[]) o;

            // o 자체는 Object 형 객체
            System.out.println("o = " + o);
            System.out.println("results(username) = " + results[0]);
            System.out.println("results(age) = " + results[1]);

            tx.commit();
        } catch (Exception e) {
            tx.rollback(); // 오류 발생 시 롤백
            e.printStackTrace(); // 에러 내용 출력
        } finally {
            //종료
            em.close();
        }
        emf.close();
    }
}
Hibernate: 
    /* insert jpql.Member
        */ insert 
        into
            Member
            (age, TEAM_ID, username, id) 
        values
            (?, ?, ?, ?)
Hibernate: 
    /* select
        m.username,
        m.age 
    from
        Member m */ select
            member0_.username as col_0_0_,
            member0_.age as col_1_0_ 
        from
            Member member0_
o = [Ljava.lang.Object;@67f77f6e
result(username) = member1
result(age) = 10

 

Object[ ] 로 조회하는 경우

 참고로 위에서 프로젝션을 할때, m.username, m.age처럼 여러개의 타입을 동시에 조회하는 경우

단순 Object형 (위 코드에서는 Object o)으로 값을 받아서 이를 Object[ ]으로 배열로 형 변환을 해주어야 했다.

 

  1. List resultList = em.createQuery(" ").getResultList( );  // 조회할 타입을 명시하지 않았으므로 단순 List로 조회
  2. Object o = resultList.get(index);  // 리스트에서 인덱스로 특정 스칼라 타입들의 모음([username,age])을 get 함.
  3. Object[ ] results = (Object[ ]) o;  // 단순 Object 객체에서 배열로 형 변환
  4. results[0] <- username   /   result[1] <- age

 근데 여기서 배열로 형 변환 이 과정이 매우 귀찮다. 그래서 다음과 아예 em.createQuery().getResultList(); 할 때 반환 타입을 List<Object[ ]>로 받는다.

 

비교

1) 쿼리 결과를 기존 처럼 List로 받는 경우

// 반환형이 List인 경우
List resultList = em.createQuery("select m.username, m.age from Member m")
        .getResultList();
// Object 객체로 get()
Object o = resultList.get(0);

// 배열형으로 형 변환
Object[] results = (Object[]) o;

// 출력
System.out.println("results(username) = " + results[0]);
System.out.println("results(age) = " + results[1]);

2) 쿼리 결과를 List<Object[ ]> 배열로 받는 경우

// 반환형을 List가 아닌 List<Object[]>로 함
List<Object[]> resultList = em.createQuery("select m.username, m.age from Member m").getResultList();
// 한번에 배열로 get()
Object[] results = resultList.get(0);

// 출력
System.out.println("results(username) = " + results[0]);
System.out.println("results(age) = " + results[1]);

new 명령어로 조회

 new를 통해서 객체로 값을 받아서 객체의 getter를 통해서 조회하는 방법도 있다.

MemberDTO.class

package jpql;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class MemberDTO {

    private String username;
    private int age;
}

JpqlMain.class

package jpql;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import java.util.List;

public class JpqlMain {
    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);

            em.flush();
            em.clear();

            // 객체를 통한 조회=> select new 조회 시에 패키지명.조회필드를 담은 클래스 from 엔티티
            List<MemberDTO> resultList =   
                    em.createQuery("select new jpql.MemberDTO(m.username, m.age) from Member m", MemberDTO.class)
                            .getResultList();
            // 조회된 값이 담긴 DTO 객체
            MemberDTO memberDTO = resultList.get(0);
            
            // 객체에서 getter로 값 꺼내기
            int age = memberDTO.getAge();
            String username = memberDTO.getUsername();
            
            tx.commit();
        } catch (Exception e) {
            tx.rollback(); // 오류 발생 시 롤백
            e.printStackTrace(); // 에러 내용 출력
        } finally {
            //종료
            em.close();
        }
        emf.close();
    }
}

 근데 이거 쓰려면 쿼리 문에 패키지명을 포함한 전체 클래스명을 입력해줘야함. 그리고 조회하고자 하는 값들의 순서에 맞는 생성자가 필요함.

'Spring > JPA' 카테고리의 다른 글

JPA- 조인  (0) 2023.05.14
JPA- 페이징  (0) 2023.05.13
JPA- 기본 문법과 쿼리 API  (0) 2023.05.07
JPA- 값 타입 컬렉션  (0) 2023.04.30
JPA- 값 타입의 비교  (0) 2023.04.30