[Spring] JPQL 기초 (2)

이연우·2025년 8월 20일

TIL

목록 보기
96/100

🧩 Embedded Type (임베디드 타입)이란?

  • JPA는 새로운 값 타입을 @Embeddable, @Embedded로 직접 정의 가능
  • 임베디드 타입도 int, String처럼 값 타입
  • 재사용성↑, 응집도↑, 단일 책임 원칙 준수

📌 예시 코드:

@Entity
public class Tutor {
    @Id @GeneratedValue
    private Long id;
    private String name;

    // Embedded 사용
    @Embedded
    private Period workPeriod;
}

@Embeddable
public class Period {
    @Temporal(TemporalType.DATE)
    Date startDate;

    @Temporal(TemporalType.DATE)
    Date endDate;

    public boolean isWork(Date date) {
        return (startDate == null || !date.before(startDate)) &&
               (endDate == null || !date.after(endDate));
    }
}

✅ 장점

  • 여러 엔티티에서 공통 타입으로 재사용 가능
  • 관련 로직을 한곳에 모아 응집도 강화
  • 독립적 메서드 작성 가능

🎯 Projection (프로젝션)이란?

  • 엔티티 전체가 아닌 특정 필드만 조회
    → 필요한 데이터만 가져와 성능 최적화 + 네트워크 비용 절감

📌 종류

1. Entity 프로젝션

List<Tutor> tutors = em.createQuery("select t from Tutor t", Tutor.class).getResultList();
  • 영속성 컨텍스트 관리 O
  • 연관 엔티티 조회 시 JOIN 사용 가능

⚠️ 묵시적 JOIN(자동)은 SQL 예측 어려움 → 명시적 JOIN 권장

Company c1 = em.createQuery("select t.company from Tutor t", Company.class).getSingleResult(); // 묵시적
Company c2 = em.createQuery("select t from Tutor t join t.company", Company.class).getSingleResult(); // 명시적

2. Embedded 프로젝션

List<Period> list = em.createQuery("select t.period from Tutor t", Period.class).getResultList();
  • select period from Period ❌ (임베디드 자체를 바로 조회 불가)
  • 조건에서 사용 가능
    • select t from Tutor t where t.period.startDate < ?
  • 상속(@MappedSuperclass) 활용 시 더 직관적

3. Scalar(스칼라) 프로젝션

List resultList = em.createQuery("select t.name, t.age from Tutor t").getResultList();
Object[] result = (Object[]) resultList.get(0);
System.out.println(result[0] + ", " + result[1]);
  • 단일 값이나 여러 값(배열) 반환
  • DTO 매핑으로 변환 가능 → 실무에서 자주 사용

📊 Projection 정리

종류설명특징
Entity엔티티 전체 조회영속성 컨텍스트 관리됨
Embedded@Embeddable 타입 조회조건문에서도 활용 가능
Scalar특정 필드만 조회배열(Object\[]) 또는 DTO로 매핑 가능

📑 Paging (페이징 처리)

  • 대량 데이터 중 일부만 가져오기 위해 결과 범위를 제한하는 기능

📄 JPQL 페이징 메서드

  • setFirstResult(int startPosition) → 조회 시작 위치
  • setMaxResults(int maxResult) → 조회할 데이터 수

📌 예시 코드:

List<Tutor> tutors = em.createQuery(
        "select t from Tutor t order by t.age desc", Tutor.class)
    .setFirstResult(5)   // 6번째부터
    .setMaxResults(10)   // 10개 조회
    .getResultList();

for (Tutor t : tutors) {
    System.out.println(t.getId() + ", " + t.getName() + ", " + t.getAge());
}
  • 나이 순으로 내림차순 정렬 후 6번째부터 10개 가져옴
  • MySQL → limit, ORACLE → ROWNUM 사용

🧠 요약 정리

구분핵심 내용
Embedded Type@Embeddable, @Embedded로 값 타입 정의
· 여러 엔티티에서 재사용 가능
· 응집도 ↑, 단일 책임 원칙 준수
· 독립적 로직 작성 가능
Projection 정의엔티티 전체가 아닌 특정 필드만 조회 → 성능 최적화 & 네트워크 비용 절감
Entity Projectionselect t from Tutor t
· 영속성 컨텍스트 관리됨
· 연관 엔티티는 JOIN으로 조회
· ⚠ 묵시적 JOIN 대신 명시적 JOIN 권장
Embedded Projectionselect t.period from Tutor t
· Embedded 자체만 단독 조회 불가
· 조건문에서 활용 가능 (t.period.startDate < ?)
· 상속(@MappedSuperclass)이 더 직관적
Scalar Projectionselect t.name, t.age from Tutor t
· 단일/여러 필드만 조회
· Object[] 반환, DTO 매핑 가능
Paging대량 데이터 중 일부만 조회
· setFirstResult(start) → 시작 위치
· setMaxResults(max) → 조회 개수 제한
· DB 방언에 맞게 페이징 SQL 변환 (MySQL=LIMIT, Oracle=ROWNUM)

0개의 댓글