[JPA] JPQL의 프로젝션, 페이징

2024. 12. 24. 15:27·JPA

 

 

프로젝션

프로젝션이란? SELECT 절에 조회할 대상을 지정하는 것이다.

프로젝션 대상은 크게 세가지로 나눌 수 있는데, 엔티티 타입, 임베디드 타입, 스칼라 타입이 있다.

  • SELECT m FROM Member m : 엔티티 프로젝션
  • SELECT m.address FROM Member m: 임베디드 타입 프로젝션
  • SELECT m.username, m.age FROM Member m: 스칼라 타입 프로젝션

예제를 통해 알아보자.

        @Autowired
        EntityManagerFactory emf;
	    EntityManager em = emf.createEntityManager();
        EntityTransaction tx = em.getTransaction();
        tx.begin();

        Team team1 = new Team();
        team1.setName("teamA");
        em.persist(team1);

        Member member1 = new Member();
        member1.setUsername("member1");
        member1.setAge(20);
        member1.setTeam(team1);
        em.persist(member1);

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

만약 이런 코드가 있다고 할 때 List 안에 담긴 Member는 영속성 컨텍스트에 담길까?

            em.flush();
            em.clear();
            Member findMember = resultList.get(0);
            findMember.setAge(20);

영속성 컨텍스트를 Flush하고 clear을 해서 비운뒤 List에 담긴 Member의 나이를 변경해보았다.

Hibernate: 
    update
        member m1_0 
    set
        age=20 
    where
        m1_0.age<=?

실행결과에서 볼 수 있듯이 업데이트 쿼리가 나가므로 엔티티 프로젝션으로 조회하면 모두 영속성 컨텍스트에 담긴다. 

 

임베디드 타입은 말 그대로 엔티티 안에 선언 되어있는 임베디드를 조회하는 것이다.

// Order.java
@Entity
@Table(name = "ORDERS")
public class Order {

    @Id @GeneratedValue
    private Long id;
    private int orderMount;

    @Embedded
    private Address address;
 }
 
  // 실제 실행 코드(임베디드 타입 조회)
  List<Address> result = em.createQuery("select o.address from Order o ", Address.class).getResultList();

 

스칼라 타입

여러 필드가 나열 되어있을 때는 세가지 방법으로 조회할 수 있다.

 

1. Query 타입으로 조회

   List resultList = em.createQuery("select m.username, m.age from Member m").getResultList();
   Object o = resultList.get(0);
   Object[] objects = (Object[]) o;
   System.out.println("objects[0] = " + objects[0]);
   System.out.println("objects[1] = " + objects[1]);
   
   // 실행결과
   objects[0] = member1
   objects[1] = 20

 

2. Object[] 타입으로 조회

   List<Object[]> resultList = em.createQuery("select m.username, m.age from Member m").getResultList();
   Object[] objects = resultList.get(0);
   System.out.println("objects[0] = " + objects[0]);
   System.out.println("objects[1] = " + objects[1]);
   
   // 실행결과
   objects[0] = member1
   objects[1] = 20

 

3. new 명령어로 조회

먼저 캐스팅할 class를 생성한다. 

package jpa.practice.v0;

import lombok.Data;
@Data
public class MemberDTO {

    private String username;
    private int age;

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

    public MemberDTO() {
    }
}

그리고 new 연산자와 패키지 경로 까지 전체 class명을 입력한다 스칼라 타입을 담는다.

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

   List<MemberDTO> resultList = em.createQuery("select new jpa.practice.v0.MemberDTO(m.username, m.age) from Member m", MemberDTO.class).getResultList();
   MemberDTO memberDTO = resultList.get(0);

단점은 패키지명이 길어지면 관리나 읽기가 힘들어진다는 단점이 있다. 

 

 

페이징

JPA는 페이징을 다음 두 API로 추상화한다. 

  • .setFirstResult(int startPosition): 조회 시작 위치
  • .setMaxResult(int maxResult): 조회할 데이터 수

그냥 단순히 시작위치와 몇개를 조회할 건지만 넣어주면 JPA가 알아서 페이징 쿼리를 만들어준다. 

            for (int i = 0; i < 100; i++) {
                Member member1 = new Member();
                member1.setUsername("member" + i);
                member1.setAge(i);
                member1.setTeam(team1);
                em.persist(member1);
            }

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

            List<Member> resultList = em.createQuery("select m from Member m order by m.age desc", Member.class)
                    .setFirstResult(0)
                    .setMaxResults(10)
                    .getResultList();

            for (Member member : resultList) {
                System.out.println("member.toString() = " + member.toString());
            }

 

실행결과

Hibernate: 
    select
        m1_0.id,
        m1_0.age,
        m1_0.team_id,
        m1_0.username 
    from
        member m1_0 
    order by
        m1_0.age desc 
    offset
        ? rows 
    fetch
        first ? rows only

DB에 맞게 페이징 쿼리가 실행되는 것을 볼 수 있다.

현재 H2 데이터베이스에 연결되어 있으므로 offset과 fetch문법이 실행됐다.

만약 DB을 변경하더라도 그에 맞게 실행될 것이다. 

'JPA' 카테고리의 다른 글

[JPA] 페치조인과 일반 조인의 차이점  (1) 2024.12.27
[JPA] JPQL의 경로표현식이란?  (0) 2024.12.27
[JPA] JPA의 타입 (임베디드 타입, 값 타입, 컬렉션 타입)이란?  (1) 2024.12.20
[JPA] 프록시와 지연로딩, 즉시로딩  (0) 2024.11.26
[JPA] 연관관계 매핑  (0) 2024.11.25
'JPA' 카테고리의 다른 글
  • [JPA] 페치조인과 일반 조인의 차이점
  • [JPA] JPQL의 경로표현식이란?
  • [JPA] JPA의 타입 (임베디드 타입, 값 타입, 컬렉션 타입)이란?
  • [JPA] 프록시와 지연로딩, 즉시로딩
Economy98
Economy98
공부하고 기록하기
  • Economy98
    Economy_Dev
    Economy98
  • 전체
    오늘
    어제
    • 분류 전체보기 (74)
      • Spring Framework (11)
      • BOJ, Programmers (22)
      • Java (4)
      • JDBC (6)
      • JPA (9)
      • Spring Transaction (3)
      • Algorithm (1)
      • Web (5)
      • Projects (2)
        • 쇼핑몰 프로젝트 (0)
        • 열람실 & 도서관 프로젝트 (2)
      • Network (2)
      • 나의 공부방 (5)
      • 끄적끄적 (1)
      • Error Log (3)
      • CS (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
    • Github
  • 링크

    • Github
  • 공지사항

  • 인기 글

  • 태그

    스프링
    자바 문제 풀이
    자바
    백준 풀이
    백준
    정렬
    Spring
    브루트포스 알고리즘
    트랜잭션
    그리디 알고리즘
    java
    예외 처리
    자바 문제
    JPA
    백준 자바 풀이
    다이나믹 프로그래밍
    jdbc
    스프링부트
    restful api
    propagation
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.1
Economy98
[JPA] JPQL의 프로젝션, 페이징
상단으로

티스토리툴바