JVM/JPA

JPQL 기초

kyoulho 2023. 6. 25. 17:37

특징

  • 객체지향 쿼리 언어다. 따라서 테이블을 대상으로 쿼리 하는 것이 아니라 엔티티 객체를 대상으로 쿼리 한다.
  • SQL을 추상화해서 특정 데이터베이스 SQL에 의존하지 않는다.
  • JPQL은 결국 SQL로 변환된다.

SELECT 문

SELECT m FROM Member AS m where m.username = 'Hello'
SELECT m FROM Member m where m.username = 'Hello' // AS는 생략할 수 있다.
  • JPQL 키워드는 대소문자를 구분하지 않고 엔티티와 필드는 대소문자를 구분한다.
  • JPQL은 별칭을 필수로 사용해야 한다.

 

TypeQuery, Query

반환할 타입을 명확하게 지정할 수 있으면 TypeQuery, 반환 타입을 명확하게 지정할 수 없으면 Query 객체를 사용

query.getResultList() 결과를 예제로 반환한다. 결과가 없으면 빈 컬렉션을 반환한다.
query.getStingleResult() 결과가 정확히 하나일 때 사용한다.
결과가 없으면 javax.persistence.NoResultException 발생
결과가 하나 이상이면 javax.persistence.NonUniqueResultException 발생
TypedQuery<Member> query = em.createQuery("SELECT m FROM Member m", Member.class);
List<Member> resultList = query.getResultList();


Query query1 = em.createQuery("SELECT m.username, m.age FROM Member m");
List resultList1 = query1.getResultList();
// 조회 대상이 둘 이상이면 Object []
for (Object o : resultList1) {
    Object[] result = (Object[]) o;
}

Query query2 = em.createQuery("SELECT m.username FROM Member m");
List resultList2 = query1.getResultList();
// 조회 대상이 하나면 Object
for (Object o : resultList2) {
    Object result = (Object) o;
}

 

 

파라미터 바인딩

이름 기준 파라미터 바인딩 방식이 더 명확하고 성능이 더 좋다..

// 이름 기준 파라미터
String usernameParam = "User1";
List<Member> resultList =
        em.createQuery("SELECT m FROM Member m WHERE m.username = :username", Member.class)
               .setParameter("username", usernameParam).getResultList();
               
// 위치 기준 파라미터
List<Member> members = 
		em.createQuery("SELECT m FROM Member m where m.username = ?1", Member.class)
                .setParameter(1, usernameParam).getResultList();

 

프로젝션

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

// 엔티티 프로젝션: 조회한 엔티티는 영속성 컨텍스트에서 관리한다.
SELECT m FROM Member m
SELECT m.team FROM Member m

// 임베디드 타입 프로젝션: 임베디드 타입은 영속성 컨텍스트에서 관리되지 않는다.
String query = "SELECT o.address FROM Order o"
List<Address> addresses = em.createQuery(query, Address.class).getResultList();
                            
// 스칼라 타입 프로젝션
Double orderAmoutAvg = 
	em.createQuery("SELECT AVG(o.orderAmount) FROM Order o". Double.class).getSingleResult();
        

// 여러 값 조회
List<Object[]> resultList = 
	em.createQuery("SELECT o.member, o.product, o.orderAmount FROM Order o").getResultList();

// DTO 바인딩
// NEW 명령어를 사용할 때는 패키지명을 포함하여야 하며 순서와 타입이 일치하는 생성자가 필요하다.
List<UserDTO> query = 
	em.createQuery("SELECT new jpabook.jpql.UserDTO(m.username, m.age)
    				FROM Member m", UserDTO.class).getResultList();

 

페이징 API

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

setFirstResult(int startPosition) 조회 시작 위치, 0부터 시작한다.
setMaxResults(int maxResult) 조회할 데이터 수
// 11~30번 데이터를 조회한다
List<Member> query = 
	em.createQuery("SELECT m FROM Member m ORDER BY m.username DESC", Member.class)
                .setFirstResult(10)
                .setMaxResults(20)
                .getResultList();

 

집합 함수

함수 설명 반환 타입
COUNT 결과 수를 구한다. Long
MAX, MIN 최대, 최소 값을 구한다. 문자, 숫자, 날짜 등에 사용한다.  
AVG 평균값을 구한다. 숫자타입만 사용할 수 있다. Double
SUM 합을 구한다. 숫자타입만 사용할 수 있다.

정수합 Long
소수합: Double
BigInterger합: BigInterger
BigDecimal합: BigDecimal

참고 사항

  • Null 값은 무시하므로 통계에 잡히지 않는다.
  • 값이 없는데 SUM, AVG, MAX, MIN 함수를 사용하면 NULL 값이 된다. COUNT는 0이 된다.
  • DISTINCT를 집합 함수 안에 사용해서 중복된 값을 제거하고 나서 집합을 구할 수 있다.
  • DISTINCT를 COUNT에서 사용할 때 임베디드 타입은 지원하지 않는다.

 

GROUP BY, HAVING, ORDER BY

String query = "SELECT t.name, COUNT(m.age) AS cnt
				FROM Member m LEFT JOIN m.team t
				GROUP BY t.name
				HAVING AVG(m.age) >= 10
				ORDER BY cnt DESC";

List<Object []> result = em.createQuery(query).getResultList();