JPA 객체지향 쿼리 언어(JPQL) - 프로젝션(SELECT)

2022. 7. 17. 18:58·공부/JPA
반응형

출처 : 자바 ORM 표준 JPA 프로그래밍 인프런 강의

 

JPA 게시글은 대부분 인프런의 김영한님의 강의인 '자바 ORM 표준 JPA 프로그래밍' 기반으로 내용을 정리했습니다.

 

프로젝션(SELECT)

  • SELECT 절에 조회할 대상을 지정하는 것
  • 프로젝션 대상 : 엔티티, 임베디드 타입, 스칼라 타입(숫자, 문자 등 기본 데이터 타입)
  • SELECT m FROM Member m  → 엔티티 프로젝션
Member member = new Member();
member.setUsername("member1");
member.setAge(10);
em.persist(member);

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

일반적인 조회 방법은 여태 써왔던 방식 그대로 사용하면 된다. 근데 여기서 한 가지 의문점이 든다. 저렇게 다건을 조회해와도 영속성 컨텍스트에 모두 올라갈까?

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

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

Member findMember = result.get(0);
findMember.setAge(20);

처음 예제에서 추가로 코드를 넣어줬다. 먼저 영속성 컨텍스트를 모두 비우고 Member를 조회한다. 그리고 나서 Member를 하나 가져오고 그 Member의 Age를 바꿔준다. 만약 UPDATE 쿼리가 나간다면 영속성 컨텍스트에서 관리가 되고 있는 것이고, 나가지 않는다면 영속성 컨텍스트에 없다는 말이다.

Hibernate: 
    /* update
        jpql.Member */ update
            Member 
        set
            age=?,
            TEAM_ID=?,
            username=? 
        where
            id=?

실행 후 출력 로그를 확인해보면 UPDATE 쿼리가 잘 나가는 것을 볼 수 있다. JPA에서 1개든 10개든 100개든 조회해오는 모든 데이터는 영속성 컨텍스트로 올라간다.

 

  • SELECT m.team FROM Member m → 엔티티 프로젝션
List<Team> result = em.createQuery("select m.team from Member m", Team.class)
        .getResultList();

코드를 위와 같이 수정한다. 조회를 해 오는 데이터가 m.team이므로 Team.class로 변경해줘야한다.  그리고 실행을 해보면 JOIN 쿼리가 나간다.

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

뭔가 나는 JPQL을 심플하게 작성했는데 쿼리는 복잡하게 나간다. 좋은 기능 같지만 JPQL도 최대한 쿼리와 비슷하게 작성해야 함이 맞다. JOIN의 경우엔 성능과 직접적으로 연관되어 있는 기능이기도 하고 튜닝도 할 수 있기 때문에 한 눈에 보여야한다. 그래서 코드를 아래와 같이 수정해주면 된다.

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

 

  • SELECT m.address FROM Meber m → 임베디드 타입 프로젝션
List<Address> result = em.createQuery("select o.address from Order o", Address.class)
        .getResultList();

임베디드 타입 프로젝션은 특이한 점은 없지만 한계점이 존재한다.

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

위와 같이 Address 엔티티 대상으로 JPQL을 작성할 수 없다. 왜냐하면 Address는 어떤 다른 객체에 소속되어 있기 때문에 단독으로 사용이 불가능하다.

 

  • SELECT m.username, m.age FROM Member m → 스칼라 타입 프로젝션
em.createQuery("select m.username, m.age from Member m")
        .getResultList();

이 스칼라 타입 프로젝션이 일반 SQL의 스칼라 프로젝션 타입과 비슷하다. 내용이 많으므로 [여러 값 조회]에서 후술한다.

 

  • DISTINCT로 중복 제거
em.createQuery("select distinct m.username, m.age from Member m")
        .getResultList();

SELECT 다음에 DISTINCT 키워드를 넣어주면 된다.

 

 

여러 값 조회

em.createQuery("select distinct m.username, m.age from Member m")
        .getResultList();

스칼라 타입에는 지금 username과 age 두 개의  필드가 존재한다. 이 데이터를 어떻게 가져와야 할까?

  • Query 타입 조회
List resultList = em.createQuery("select distinct m.username, m.age from Member m")
        .getResultList();

Query 타입의 반환 값은 List이다. List에는 특정 타입을 넣을 수 없으니 Object로 돌려준다. 그래서 값을 가져올 때는 아래와 같이 형변환을 통해 값을 가져와야 한다.

Object o = resultList.get(0);
Object[] result = (Object[]) o;
System.out.println("result = " + result[0]);
System.out.println("result = " + result[1]);
// 출력 로그
Hibernate: 
    /* select
        distinct m.username,
        m.age 
    from
        Member m */ select
            distinct member0_.username as col_0_0_,
            member0_.age as col_1_0_ 
        from
            Member member0_
result = member1
result = 10

 

  • Object[] 타입으로 조회
List<Object[]> resultList = em.createQuery("select distinct m.username, m.age from Member m")
        .getResultList();

Object[] result = resultList.get(0);
System.out.println("result = " + result[0]);
System.out.println("result = " + result[1]);

List 제네릭에 Object[]를 넣어주면 형변환 작업을 생략할 수 있다.

 

  • new 명령어로 조회
    • 이게 제일 깔끔한 방법이다.
    • 단순 값을 DTO로 바로 조회
    • 패키지명을 포함한 전체 클래스 명 입력
    • 순서와 타입이 일치하는 생성자가 필요하다.

먼저 MemberDTO 클래스를 생성한다.

public class MemberDTO {

    private String username;
    private int age;
    
    public MemberDTO(String username, int age) {
        this.username = username;
        this.age = age;
    }
    // getter/setter 생략
}

그리고 실행 클래스에서 코드를 수정해준다.

List<MemberDTO> result = em.createQuery("select new jpql.MemberDTO(m.username, m.age) from Member m", MemberDTO.class)
        .getResultList();

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

뭔가 되게 원하는 대로 작동이 되지만 뭔가 쿼리에 구구절절 패키지명을 적어야 하는게 맞나 싶기도 하다. 근데 추후 QueryDSL을 사용하면 이런 불편한 부분을 극복할 수 있게 된다.

 

반응형
저작자표시 비영리 변경금지 (새창열림)
'공부/JPA' 카테고리의 다른 글
  • JPA 객체지향 쿼리 언어(JPQL) - 조인(JOIN)
  • JPA 객체지향 쿼리 언어(JPQL) - 페이징 API
  • JPA 객체지향 쿼리 언어(JPQL) - 기본 문법과 쿼리 API
  • JPA 객체지향 쿼리 언어 - JPQL, QueryDSL 등 소개
데부한
데부한
어차피 할 거면 긍정적으로 하고 싶은 개발자
    반응형
  • 데부한
    동동이개발바닥
    데부한
  • 전체
    오늘
    어제
    • 분류 전체보기 (307)
      • 방통대 컴퓨터과학과 (27)
        • 잡담 (9)
        • 3학년1학기 (17)
      • 프로젝트 및 컨퍼런스 회고 (1)
        • 프로젝트 (4)
        • 한이음 프로젝트 (0)
        • 회고 (3)
      • 공부 (165)
        • Spring (37)
        • JPA (71)
        • 인프런 워밍업 클럽_BE (10)
        • Java (6)
        • React.js (27)
        • 넥사크로 (11)
        • 기타 (3)
      • 알고리즘 (85)
        • 알고리즘 유형 (10)
        • 알고리즘 풀이 (57)
        • SQL 풀이 (18)
      • 에러 해결 (13)
      • 잡담 (7)
        • 국비교육 (2)
        • 구매후기 (5)
        • 진짜 잡담 (0)
  • 블로그 메뉴

    • Github
    • Linkedin
    • 홈
    • 방명록
    • 글쓰기
    • 관리
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    SpringBoot를 이용한 RESTful Web Service 개발
    MSA
    JPA
    토이프로젝트
    react
    코딩테스트
    알고리즘
    SQL
    자바스크립트
    egov
    기출문제
    토비의스프링부트
    인프런
    백준
    에러해결
    넥사크로
    Spring
    스프링부트
    oracle
    QueryDSL
    방통대
    Java
    springboot
    운영체제
    프로그래머스
    개발자
    프론트엔드
    IT
    RESTful
    전자정부프레임워크
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
데부한
JPA 객체지향 쿼리 언어(JPQL) - 프로젝션(SELECT)
상단으로

티스토리툴바