특징
- 객체지향 쿼리 언어다. 따라서 테이블을 대상으로 쿼리 하는 것이 아니라 엔티티 객체를 대상으로 쿼리 한다.
- 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();
728x90
'JVM > JPA' 카테고리의 다른 글
JPQL 서브쿼리 (0) | 2023.06.26 |
---|---|
JPQL 조인과 페치조인 (0) | 2023.06.25 |
값 타입 컬렉션 @ElementCollection @CollictionTable (0) | 2023.06.25 |
임베디드 타입(복합 값 타입) @Embedded, @Embeddable (0) | 2023.06.24 |
영속성 전이, 고아 객체 (0) | 2023.06.23 |