객체지향 쿼리 언어1 - 기본 문법

이재표·2023년 10월 10일
0
List<Member> result = em.createQuery("select m from Member m", Member.class)
                    .getResultList();

엔티티 프로젝션하면 여러개의 엔티티가 있어도 다 영속성 관리가 된다

JPQL

jpql은 "Java Persistence Query Language"의 줄임말로 객체지향 쿼리 언어이다. 따라서 데이터베이스의 테이블을 대상으로 쿼리 하는것이 아닌 엔티티 객체를 대상으로 쿼리한다.

하지만 결국 데이터베이스로 쿼리를 해야하기 때문에 SQL로 변환된다.

JPQL문법

select절
from절
  [where]
  [groupby]
  [having]
  [orderby]
-------------
update
  [where]
-------------
delete
  [where]

보는것과 같이 SQL과 유사한 형태라는 것을 알수 있다.

주의점
select m from Member as m where m.age>18

  • 엔티티와 속성은 대소문자 구분해야한다.
  • JPQL키워드는 대소문자를 구분하지 않는다.
  • 테이블명이 아닌 엔티티 이름(@Entity(name=""))을 사용한다.
  • 별칭은 필수이다!!(as는 생략 가능)

사용법

JPQL은 em.createQuery를 통해 사용할수 있다.

em.craeteQuery("select m from Member m");

이때 두가지 타입이 존재한다.
TypeQuery : 반환 타입이 명확할때 사용

TypedQuery<Member>query=em.craeteQuery("select m from Member m",Member.class);

Query : 반환 타입이 명확하지 않을때 사용

Query query=em.craeteQuery("select m from Member m");

또한 쿼리를 한 다음, 결과를 조회할때 결과 개수에 따른 메서드가 존재한다.

query.getResultList()
결과가 하나 이상일 때, 리스트 반환

  • 결과가 없으면 빈 리스트 반환

query.getSingleResult()
결과가 정확히 하나, 단일 객체 반환

  • 결과가 없으면: javax.persistence.NoResultException
  • 둘 이상이면: javax.persistence.NonUniqueResultException

또한 좀 더 동적으로 사용하기 위하여 JPQL에서는 파라미터 바인딩을 지원한다. 이때 이름기준, 위치기준에 따라 사용법이 조금 달라진다.
이름기준

SELECT m FROM Member m where m.username=:username
query.setParameter("username", usernameParam);

위치기준

SELECT m FROM Member m where m.username=?1
query.setParameter(1, usernameParam);

프로젝션

select 절에서 조회할 대상을 지정하는 것을 프로젝션이라 한다.
프로젝션에는 여러 방법이 존재한다.

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

이때 스칼라타입 프로젝션의 경우 여러 값을 한번에 조회하므로 리스트로 출력되게 되는데, 이때 조회 방식 3가지가 존재한다.
1. Query타입 조회
2. Object[]타입으로 조회
3. dto를 통해 생성자로 조회

단순 값을 DTO로 바로 조회
SELECT new jpabook.jpql.UserDTO(m.username, m.age) FROM
Member m
• 패키지 명을 포함한 전체 클래스 명 입력
• 순서와 타입이 일치하는 생성자 필요

페이징 API

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

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

조회시작 위치를 정한다음 조회할 데이터의 수를 알고있으니 그만큼 조회해주면 페이징 처리가 되게 된다.

//페이징 쿼리
 String jpql = "select m from Member m order by m.name desc";
 List<Member> resultList = em.createQuery(jpql, Member.class)
   .setFirstResult(10)
   .setMaxResults(20)
   .getResultList();

조인

객체스타일로 조인이 나간다.

대표적으로 세가지 조인을 볼수 있다

  • 내부 조인
    SELECT m FROM Member m [INNER] JOIN m.team t
  • 외부 조인
    SELECT m FROM Member m LEFT [OUTER] JOIN m.team t
  • 세타 조인
    select count(m) from Member m, Team t where m.username
    = t.name

이때 on절을 활용하여 조인 대상을 필터링하거나 연관관계 없는 엔티티 외부 조인을 지원한다.

//회원과 팀을 조인하면서, 팀이름이 A인 팀만 조인 (필터링)
[JPQL]
SELECT m,t FROM Member m LEFT JOIN m.team t on t.name='A'
[SQL]
SELECT m.*, t.* FROM Member m LEFT JOIN t ON m.TEAM_ID=t.id and t.name='A'
//회원의 이름과 팀의 이름이 같은 대상을 외부조인(연관관계 없는 엔티티 외부조인)
[JPQL]
SELECT m,t FROM Member m LEFT JOIN Team t on m.username=t.name
[SQL]
SELECT m.*, t.* FROM Member m LEFT JOIN Team t ON m.username=t.name

이때 jpql에서는 쿼리안에 또 다른 쿼리를 넣을수 있는 서브쿼리를 지원해준다.
이때 서브쿼리는 where과 having 절에서만 사용가능하다. select절 또한 하이버네이트에서 지원하지만 from절의 서브쿼리는 불가하지만 조인으로 풀어서 해결가능하다.

나이가 평균보다 많은 회원
select m from Member m
where m.age > (select avg(m2.age) from Member m2)

한 건이라도 주문한 고객
select m from Member m
where (select count(o) from Order o where m = o.member) > 0

0개의 댓글