ABOUT ME

Today
Yesterday
Total
  • JPQL 이란?
    프로그래밍 기초 공부 2023. 3. 30. 02:35

    JPQL

    • Java Persistence Query Language의 약자
    • JPA의 일부로 정의된 플랫폼 독립적인 객체지향 쿼리 언어임
    •  JPA는 엔티티 객체를 중심으로 개발하기 때문에 SQL을 사용하지 않음
    • 엔티티 객체를 대상으로 쿼리를 작성함

     

    JPQL 특징

    • JPQL은 ANSI 표준에서 지원하는 쿼리 명령문을 모두 제공
      •  ex) SELECT, FROM, WHERE, GROUP BY, HAVING, JOIN 등
    • JPQL은 SQL을 추상화하여 객체 중심적, 객체 지향적 쿼리 언어
      • 테이블이 아닌 객체를 대상으로 검색을 수행하는 쿼리 언어
      •  JPQL과 SQL을 구분할 수 있는 특성
    • JPQL은 SQL을 추상화한 것으로, 특정 DB에 의존적 또는 종속적이지 않다. 
      • DB 사용에 있어서 자유롭다. 
    • JPA를 통해 작성된 JPQL은 SQL로 번역된다. 

     

    JPQL과 SQL 차이점

    • JPQL은 엔티티 객체를 대상으로 쿼리함
    • SQL은 DB 테이블을 대상으로 쿼리

     

    JPQL 사용 이유

    • JPA는 객체 중심적으로 코드를 작성함
    •  검색(select)에서 한계에 부딪힘
    •  모든 DB 테이블을 객체로 변환하여 검색해야함
    • SELECT 쿼리를 수행할 때 함께 쓰이는 조건문을 작성하는 것에 한계를 보임

     

    JPQL 문제점

    • JPQL은 문자열(=String) 형태이기 때문에 개발자 의존적 형태
    • 동적으로 쿼리 언어를 작성하는 데 효율적이지 못함
    • Compile 단계에서 Type-Check가 불가능 
    • RunTime 단계에서 오류 발견 가능 (장애 risk 상승)

     

     

    JPQL 문법

    JPQL 문법은 SQL 문법과 매우 유사함

     

    특징

    • 엔티티와 속성은 대소문자를 구분한다. (Member(엔티티), age(속성))
      • @Entity의 name을 지정하지 않으면, 클래스 이름이 엔티티 이름임
    • JPQL 키워드는 대소문자를 구분하지 않음 (Select, FROM, where)
    • 테이블 이름이 아닌 엔티티 이름을 사용함(Member, Team)
    • 별칭(m) 사용은 필수적
    • as는 생략이 가능함
    • GROUP BY, HAVING, ORDER BY 등 모두 사용이 가능

     

     

    TypeQuery, Query

    • JPQL을 실행하려면 쿼리 객체를 만들어야 함
    • 쿼리 객체로는 TypeQuery와 Query 존재
    • 반환할 타입을 명확하게 지정할 수 있으면 TypeQuery 객체
    • 명확하게 지정할 수 없으면 Query 객체를 사용
    • TypeQuery 사용
    TypedQuery<Member> query =
        em.createQuery("select m from Member m", Member.class);
        
    List<Member> resultList = query.getResultList();
    
    for (Member member resultList) {
        System.out.println("member : " + member);
    }

     

    Query 사용

    Query query = 
        em.createQuery("select m.username, m.age from Member m");
        
    List resultList = query.getResultList();
    
    for (Object o : resultList) {
        Object[] result = (Object[]) o; // 결과가 둘 이상이면 Object[] 반환
        System.out.println("username : " + result[0]);
        System.out.println("age : " + result[1]);
    }

     

    •  Query 객체는 SELECT 문의 조회 대상이 둘 이상이면 Object[]를 반환
    • 타입을 변환할 필요가 없는 TypeQuery를 사용하는 것이 더 편리함

     

     

     

    파라미터 바인딩

    • 이름 기준 파라미터와 위치 기준 파라미터 존재
    • 위치 기준 파라미터보단 이름 기준 파라미터가 더 명확함

     

     

    1. 이름 기준 파라미터

    •  이름 기준 파라미터는 파라미터를 이름으로 구분하는 방법
    String param = "leveloper";
    
    TypedQuery<Member> query =
        em.createQuery("select m from Member m where m.username = :username", Member.class);
        
    query.setParameter("username", param);
    
    List<Member> resultList = query.getResultList();

     

     

    2. 위치 기준 파라미터

    • 위치 기준 파라미터를 사용하려면 ? 다음에 위치 값을 주어줌
    • 위치 값은 1부터 시작
    String param = "leveloper";
    
    List<Member> members =
        em.createQuery("select m from Member m where m.username = ?1", Member.class)
        .setParameter(1, param)
        .getResultList();

     

     

     

     

    프로젝션

    • SELECT 절에 조회할 대상을 지정하는 것을 프로젝션
    • 프로젝션 대상에는 엔티티, 임베디드 타입, 스칼라 타입

     

    1. 엔티티 프로젝션

    • 원하는 객체를 바로 조회하는 것이 컬럼을 하나하나 나열해서 조회해야 하는 SQL과는 차이가 있음
    • 조회한 엔티티는 영속성 컨텍스트에서 관리됨
    String query = "SELECT m FROM Member m";
    
    List<Member> memberList = em.createQuery(query, Member.class)
                                .getResultList();

     

     

    2. 임베디드 타입 프로젝션

    •  JPQL에서 임베디드 타입은 엔티티와 거의 비슷하게 사용
    • 임베디드 타입은 조회의 시작점이 될 수 없다는 제약이 있음
    • 임베디드 타입은 엔티티 타입이 아닌 값 타입임
    • 직접 조회한 임베디드 타입은 영속성 컨텍스트에서 관리되지 않음
    String query = "SELECT o.address FROM Order o";
    
    List<Address> addressList = em.createQuery(query, Address.class)
                                  .getResultList();

     

     

    3. 스칼라 타입 프로젝션

    •  숫자, 문자, 날짜와 같은 기본 데이터 타입들을 스칼라 타입
    • 통계 쿼리도 주로 스칼라 타입으로 조회함
    List<String> usernameList =
        em.createQuery("select username from Member m", String.class)
          .getResultList();

     

     

    4. new 명령어

    • 꼭 필요한 데이터들만 선택해서 조회해야 할 때도 있음
    • 이럴 때는 UserDTO처럼 의미 있는 객체로 변환해서 사용
    public class UserDTO{
        
        private String username;
        private int age;
        
        public UserDTO(String username, int age){
            this.username = username;
            this.age = age;
        }
    }
    
    
    TypedQuery<UserDTO> query =
        em.createQuery("select new jpabook.jpql.UserDTO(m.username, m.age) from Member m", UserDTO.class);
        
    List<UserDTO> resultList = query.getResultList();

     

     

     

    페이징 API

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

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

     

    TypedQuery<Member> query =
        em.createQuery("select m from Member m order by m.username DESC", Member.class);
        
    query.setFirstResult(10);
    query.setMaxResults(20);
    
    List<Member> resultList = query.getResultList();

     

    • 데이터베이스마다 다른 페이징 처리를 같은 API로 처리할 수 있는 것은 데이터베이스 언어 덕분
    • 데이터베이스마다 SQL이 다른 것은 물론이고 오라클과 SQLServer는 페이징 쿼리를 따로 공부해야 SQL을 작성할 수 있을 정도로 복잡함
    • 페이징 SQL을 더 최적화하고 싶다면 JPA가 제공하는 페이징 API가 아닌 네이티브 SQL을 직접 사용해야 함

     

     

     

    JPQL 조인

    • SQL 조인과 기능은 같고 문법만 약간 다름

     

    내부 조인

    • JPQL 조인의 가장 큰 특징은 연관 필드를 사용함
    • 연관  필드는 다른 엔티티와 연관관계를 가지기 위해 사용하는 필드를 뜻함
    String teamName = "teamA";
    
    String query = "select m from Member m inner join m.team t where t.name = :teamName";
    
    List<Member> memberList = em.createQuery(query, Member.class)
        .setParameter("teamName", teamName)
        .getResultList();

     

    외부 조인

    • 외부 조인은 기능상 SQL의 외부 조인과 같음
    • OUTER는 생략 가능
    SELECT m
    FROM Member LEFT (OUTER) JOIN m.team t

     

     

    세타 조인

    • 세타 조인은 전혀 관계없는 엔티티도 조인할 수 있음
    • WHERE 절을 사용해서 세타 조인 가능
    SELECT count(m)
    FROM Member m, Team t
    where m.username = t.name

     

     

     

     

    페치 조인 (Fetch Join)

    • 페치 조인은 SQL 조인의 종류가 아니고 JPQL에서 성능 최적화를 위해 제공하는 기능
    • 연관된 엔티티나 컬렉션을 한 번에 같이 조회하는 기능으로 join fetch 명령어로 사용할 수 있음
    • 페치 조인은 N + 1 문제를 해결하는 데 주로 사용되는 방법
    String query = "select m from Member m join fetch m.team";
    
    List<Member> memberList = em.createQuery(query, Member.class).getResultList();
    
    for (Member member : memberList) {
        System.out.println("username : " + member.getUsername());
        System.out.println("teamname : " + member.getTeam().getName());
    }

     

     회원과 팀을 지연 로딩(LAZY)로 설정했다고 하면, 회원을 조회할 때 페치 조인을 사용해서 팀도 함께 조회했으므로 연관된 팀 엔티티는 프록시가 아닌 실제 엔티티이게 됨
    프록시가 아닌 실제 엔티티이므로 회원 엔티티가 영속성 컨텍스트에서 분리되어 준영속 상태가 되어도 연관된 팀을 조회할 수 있음

     

     

    '프로그래밍 기초 공부' 카테고리의 다른 글

    Docker 란?  (0) 2023.04.01
    Kafka란?  (0) 2023.03.31
    DAO, DTO, VO란?  (0) 2023.03.23
    Spring Annotation 정리  (0) 2023.03.13
    Stream  (0) 2023.02.02
Designed by Tistory.