공부/JPA

JPA 객체지향 쿼리 언어(JPQL) - 기본 문법과 쿼리 API

데부한 2022. 7. 16. 19:06
반응형

출처 : 자바 ORM 표준 JPA 프로그래밍 인프런 강의

 

JPA 게시글은 대부분 인프런의 김영한님의 강의인 '자바 ORM 표준 JPA 프로그래밍' 기반으로 내용을 정리했습니다.

 

기본 문법과 쿼리 API

  • JPQL? Java Persistence Query Language

 

JPQL

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

 

프로젝트 새로 생성 후

Member.class

@Entity
public class Member {

    @Id @GeneratedValue
    private Long id;
    private String username;
    private int age;

    @ManyToOne
    @JoinColumn(name = "TEAM_ID")
    private Team team;
    
    // getter/setter 생략
}

 

Team.class

@Entity
public class Team {

    @Id @GeneratedValue
    private Long id;

    private String name;

    @OneToMany(mappedBy = "team")
    private List<Member> members = new ArrayList<>();
    
    // getter/setter 생략
}

 

Address.class

@Embeddable
public class Address {

    private String city;
    private String street;
    private String zipcode;
    
    // getter/setter 생략
}

 

Order.class

@Entity
@Table(name = "ORDERS")
public class Order {

    @Id @GeneratedValue
    private Long id;
    private int orderAmount;

    @Embedded
    private Address address;
    
    @ManyToOne
    @JoinColumn(name="PRODUCT_ID")
    private Product product;
    
    // getteer/setter 생략
}

 

Product.class

@Entity
public class Product {

    @Id @GeneratedValue
    private Long id;
    private String name;
    private int price;
    private int stockAmount;
    
    // getter/setter 생략
}

 

 

JPQL 문법

  • 전체적으로 SQL과 유사하다.
select m from Member as m where m.age > 18
  • 엔티티와 속성은 대소문자를 구분한다.
  • JPQL 키워드(SELECT, FROM, where)는 대소문자를 구분하지 않는다.
  • 엔티티 이름 사용한다. MEMBER 테이블이 아니고 Member 엔티티 이름이다.
  • 별칭(m)은 필수이다. (as는 생략 가능함)

 

집합과 정렬

select
    COUNT(m), // 회원수
    SUM(m.age), // 나이, 합
    AVG(m.age), // 나이, 평균
    MAX(m.age), // 최대 나이
    MIN(m.age) // 최소 나이
from Member m
  • GROUP BY, HAVING
  • ORDER BY

다 사용이 가능하다.

 

TypeQuery, Query

  • TypeQuery : 반환 타입이 명확할 때 사용한다.
TypedQuery<Member> query = 
	em.createQuery("SELECT m FROM Member m", Member.class);
  • Query : 반환 타입이 명확하지 않을 때 사용한다.
Query query = 
	em.createQuery("SELECT m.username, m.age from Member m");
  • Query의 경우 m.username 만 가져왔을 때는 반환 타입을 String.class로 해줘도 되는데 위의 예제 코드에서는 m.age까지 가져오므로 타입을 특정할 수가 없기 때문에 Query를 사용한다.

 

결과 조회 API

  • query.getResultList() : 조회 결과가 하나 이상일 때 리스트로 반환해준다.
    • 결과가 없을 경우엔 빈 리스트를 반환한다.
TypedQuery<Member> query =
        em.createQuery("select m from Member m", Member.class);

List<Member> resultList = query.getResultList();
for (Member member1 : resultList) {
    System.out.println("member1 = " + member1);
}
  • query.getSingleResult() : 결과가 정확히 하나일 때 단일 객체로 반환해준다.
    • 결과가 없을 경우 : javax.persistence.NoResultException
    • 둘 이상이면 : javax.persistence.NonUniqueResultException
TypedQuery<Member> query =
        em.createQuery("select m from Member m", Member.class);

Member result = query.getSingleResult();
System.out.println("result = " + result);
  • 결과가 없을 경우엔 getResultList()처럼 null을 반환해주면 될 거 같은데 굳이 예외를 터트려준다. 그래서 결과가 없는 경우엔 따로 try~catch를 작성해줘야한다.
    • 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());

 

반응형