본문 바로가기
JPA

[JPA] JPQL(Java Persistence Query Language) 문법(1)

by byeongoo 2020. 4. 30.

1. JPQL(객체지향 쿼리 언어)

  • JPA를 사용하면 엔티티 객체를 중심으로 개발
  • 문제는 검색 쿼리
  • 검색을 할 때도 테이블이 아닌 엔티티 객체를 대상으로 검색
  • 모든 DB 데이터를 객체로 변환해서 검색하는 것은 불가능
  • 애플리케이션이 필요한 데이터만 DB에서 불러오려면 결국 검 색 조건이 포함된 SQL이 필요

JPA는 SQL을 추상화한 JPQL이라는 객체 지향 쿼리 언어를 지원합니다. SQL 문법과 유사한 SELECT, FROM, WHERE, GROUP BY HAVING, JOIN을 지원합니다. 기존에 SQL을 사용하셨던 분들은 쉽게 배울 수 있습니다. JPQL은 엔티티 객체를 대상으로 쿼리를 합니다. SQL은 데이터베이스 테이블을 대상으로 쿼리를 날립니다.

String jpql = "select  m from Member m where m.name like '%hello%'";

List<Member> result = em.createQuery(jpql, Member.class).getResultList();

테이블이아닌 객체를 대상으로 검색하는 객체지향 쿼리입니다. SQL을 추상화해서 특정 데이터베이스 SQL에 의존하지 않습니다. 

2. JPQL 문법

  • select m from Member as m where m.age > 18
  • 엔티티와 속성은 대소문자 구분O (Member, age)
  • JPQL 키워드는 대소문자 구분X (SELECT, FROM, where)
  • 엔티티 이름 사용, 테이블 이름이 아님(Member)
  • 별칭은 필수(m) (as는 생략가능)

TypeQuery : 반환 타입이 명확할 때 사용

TypeQuery<Member> query = em.createQuery("SELECT m From Member m", Member.class);

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

Query query = em.createQuery("SELECT m.username, m.age from Member m");

결과 조회 API

  • query.getResultList(): 결과가 하나 이상일 때, 리스트 반환. nullpointerexception을 걱정하지 않아도 됩니다.
    • 결과가 없으면 빈 리스트 반환
  • query.getSingleResult(): 결과가 정확히 하나, 단일 객체 반환
    • 결과가 없으면: javax.persistence.NoResultException 
    • 둘 이상이면: javax.persistence.NonUniqueResultException
    • Spring Data Jpa는 결과가 없으면 NULL또는 옵셔널을 반환하도록 따로 처리를 하고 있습니다.

파라미터 바인딩 - 이름 기준, 위치 기준

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 절에 조회할 대상을 지정하는 것 
    프로젝션 대상: 엔티티, 임베디드 타입, 스칼라 타입(숫자, 문자등 기본 데이터 타 입)
  • 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 -> 스칼라 타입 프로젝션
  • DISTINCT로 중복 제거

프로젝션 여러값 조회

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

영속성 컨텍스트에서 다 관리가 되기 때문에 엔티티 프로젝션이라고합니다. m.team이라고 하면 result 결과를 Team으로 받아야합니다.

Member member = new Member();
member.setUsername("member1");
member.setAge(10);
em.persist(member);

em.flush();
em.clear();

List<Team> result = em.createQuery("select m.team from Member m", Team.class).getResultList();

출력되는 쿼리를 보면 member와 team을 내부조인합니다. sql과 최대한 비슷하게 써야합니다 위에 처럼 쓰지말고 from절에서 join을 해줘서 가지고오는게 맞습니다.

List<Team> result = em.createQuery("select t from Member m join m.team t" Team.class).getResultList();

이렇게 해야 개발자가 join을 하는구나 예측이 됩니다.

 

임베디드 타입 프로젝션

em.createQuery("select o.address from Order o", Address.class).getResultList();

임베디드 타입 프로젝션의 한계는 어디에 소속 되있기 때문에 select address from Order 이런식으로는 조회가 안됩니다. 

 

스칼라 타입 프로젝션

내가 원하는 데이터만 sql 짜듯이 가지고 올 수 있습니다. 일반 sql이랑 똑같다고 생각하면 됩니다.

em.createQuery("select distinct m.username, m.age from Member m").getResultList();

 

 

깔끔한거는 new 명령어로 조회하는 것 입니다. 마치 생성자를 호출하듯이 문법이 되있습니다. 물론 생성자는 dto에 정의 되어 있어야합니다. 패키지명이 길어지면 다 적어줘야합니다. 하지만 QueryDSL을 사용하면 극복가능합니다.

List<MemberDTO> result = em.createQuery("select new jpql.MemberDTO(m.username, m.age) from Member m")
						   .getResultList(); 

MemberDTO memberDTO = result.get(0);

 

'JPA' 카테고리의 다른 글

[JPA] JPQL(Java Persistence Query Language) 문법(2)  (0) 2020.04.30
[JPA] 값 타입 컬렉션  (0) 2020.04.26
[JPA] 값타입과 불변 객체  (0) 2020.04.26
[JPA] 임베디드 타입(복합 값 타입)  (0) 2020.04.26
[JPA] 고아객체  (0) 2020.04.22