프레임워크(Framework)/JPA

[JPA] JPQL이란? - 객체지향 쿼리 언어 JPQL (1)

잇트루 2022. 12. 13. 02:26
반응형
본 내용은 온라인 강의 사이트 인프런의 김영한 님의 강의 내용이 포함되어 있습니다.
'자바 ORM 표준 JPA 프로그래밍 - 기본편'
 

자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런 | 강의

JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자신있게 JPA를 사용할 수 있습니다., - 강의 소개 | 인프런

www.inflearn.com

 

JPQL(Java Persistence Query Language)

JPA는 SQL을 추상화한 JPQL이라는 객체 지향 쿼리 언어를 제공한다.

따라서 테이블을 대상으로 쿼리 하는 것이 아닌 엔티티 객체를 대상으로 쿼리한다.

 

JPQL은 SQL을 추상화했기 때문에 특정 데이터베이스 SQL에 의존하지 않는 장점이 있다.

 

JPQL은 SQL과 문법이 유사하며, SELECT, FROM, WHERE, GROUP BY, HAVING, JOIN을 지원한다.

  • JPQL은 결국 SQL로 변환된다.

 

JPQL 사용 예시

String jpql = "select m From Member m where m.name like ‘%hello%'";
List<Member> result = em.createQuery(jpql, Member.class).getResultList();

for (Member member : result) {
    System.out.println("member = " + member);
}
  • JPQL은 테이블이 아닌 객체를 대상으로 검색하는 객체지향 쿼리 언어이다.
  • 위 코드에서는 Member 객체를 대상으로 이름에 “hello”가 포함된 모든 회원을 검색한다.

 

JPQL의 문제점

  • JPQL은 기본 문자열로 작성되기 때문에 컴파일 시 에러를 발생하지 않는다.
    • 문제가 있음에도 불구하고 정상적으로 작동하여 배포 시 문제가 발생할 수 있음

 

  • 동적으로 쿼리 언어를 작성하는 데 효율적이지 못하다.
    • 예) 특정 조건의 참일 경우엔 A SQL 쿼리를, 거짓일 경우엔 B 쿼리를 실행하는 등

 

 

JPQL과 SQL의 가장 뚜렷한 차이점은 JPQL은 엔티티 객체를 대상으로 쿼리문을 작성하며, SQL은 데이터베이스 테이블을 대상으로 쿼리문을 작성하는 것이다.

 

 

JPQL 문법

JPQL 문법은 SQL 문법과 매우 유사하다.

select _ from _ [where] _ [groupby] _ [having] _ [orderby] _

update _ [where] _

delete _ [where] _

 

JPQL 특징

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

 

관계형 데이터베이스의 집합과 정렬 등의 함수를 제공한다.

select
	COUNT(m), // Member 수
	SUM(m.age), // Member의 나이 합
	AVG(m.age), // Member의 평균 나이
	MAX(m.age), // Member의 최대 나이
	MIN(m.age) // Member의 최소 나이
from Member m
  • GROUP BY, HAVING, ORDER BY 등 모두 사용이 가능하다.

 

 

TypeQuery와 Query

TypeQuery

반환 타입이 명확할 때 사용

TypedQuery<Member> query1 = em.createQuery("select m from Member m", Member.class);
TypedQuery<String> query2 = em.createQuery("select m.username from Member m", String.class);
  • 반환 타입이 명확할 경우, 두 번째 파라미터에 타입 정보를 작성할 수 있다.
    • 기본으로 엔티티 타입을 기입한다.
    • qeury2와 같이 string인 username 정보는 String.class 타입 정보를 기입할 수 있다.

 

 

Query

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

Query query3 = em.createQuery("select m.username, m.age from Member m");
  • String 타입인 username과 int 타입인 age를 함께 조회할 때에는 반환 타입이 명확하지 않다.
  • 이 경우 Query를 사용할 수 있다.

 

 

결과 조회 API

getResultList()

  • 결과가 하나 이상일 때 사용하며, 리스트로 반환한다.
  • 결과가 없을 경우 빈 리스트를 반환한다.
TypedQuery<Member> query1 = em.createQuery("select m from Member m", Member.class);
List<Member> resultList = query1.getResultList();

for (Member m : resultList) {
    System.out.println("m = " + m.getUsername());
}

 

getSingleResult()

  • 결과가 단 하나일 경우 사용하며, 단일 객체를 반환한다.
  • 결과가 없으면 javax.persistence.NoResultException 예외가 발생한다.
  • 결과가 둘 이상이면 javax.persistence.NonUniqueResultException 예외가 발생한다.
TypedQuery<Member> query2 = em.createQuery("select m from Member m where m.id = 1L", Member.class);
Member singleResult = query2.getSingleResult();

System.out.println("singleResult = " + singleResult.getUsername());
  • Spring Data JPA를 사용할 경우 null을 반환하거나 Optional로 반환하여 처리한다.

 

 

파라미터 바인딩

이름 기준

TypedQuery<Member> query = em.createQuery("select m from Member m where m.username = :username", Member.class);
query.setParameter("username", "member1");
Member singleResult = query.getSingleResult();

System.out.println("singleResult = " + singleResult.getUsername());

 

위 코드를 메서드 체인으로 엮어서 결과를 도출할 수 있다.

Member result = em.createQuery("select m from Member m where m.username = :username", Member.class)
                .setParameter("username", "member1")
                .getSingleResult();

System.out.println("result = " + result.getUsername());

 

위치 기준

Member result = em.createQuery("select m from Member m where m.username = ?1", Member.class)
                .setParameter(1, "member1")
                .getSingleResult();

System.out.println("result = " + result.getUsername());
  • 참고로 위치 기준 파라미터 바인딩은 사용하지 않는 것이 좋다.
    • 데이터베이스에는 값이 계속해서 추가되기 때문에 위치가 변경될 가능성이 매우 높기 때문
반응형