JPA/JPQL

[JPQL] 객체지향 쿼리 언어 소개 및 JPQL 기본 문법

yoon_seon 2023. 4. 25. 17:21

📌 객체지향 쿼리 언어 소개

JPA는 다양한 쿼리 방법을 지원한다.

  • JPQL
  • JPA Criteria
  • QueryDSL
  • 네이티브 SQL
  • JDBC API 직접 사용, MyBatis, SpringJdbcTemplate 함께 사용

 

JPQL  - 소개

  • 가장 단순한 조회 방법
    • EntityManager.find()
    • 객체 그래프 탐색(a.getB().getC())
  • 나이가 18살 이상인 회원을 모두 검색하고 싶다면?

 

JPQL  - 필요성

JPA를 사용하면 엔티티 객체를 중심으로 개발한다.

문제는 검색 쿼리이다. 검색을 할 때도 테이블이 아닌 엔티티 객체를 대상으로 검색한다.

그렇다고 모든 DB 데이터를 객체로 변환해서 검색하는 것은 불가능하다.

결국 애플리케이션이 필요한 데이터만 DB에서 불러오려면 검색 조건이 포함된 SQL이 필요하다.

 

JPQL  - 특징

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

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

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

(JPQL은 엔티티 객체로 대상으로, SQL은 데이터베이스 테이블 대상으로 쿼리)

SQL을 추상화해서 특정 데이터베이스 SQL에 의존하지 않는다.

 

 

Criteria - 소개

  • 문자가 아닌 자바코드로 JPQL을 작성할 수 있음
//Criteria 사용 준비
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Member> query = cb.createQuery(Member.class);

//루트 클래스 (조회를 시작할 클래스)
Root<Member> m = query.from(Member.class);

//쿼리 생성 
CriteriaQuery<Member> cq =  query.select(m).where(cb.equal(m.get("username"), "kim"));
List<Member> resultList = em.createQuery(cq).getResultList()
  • JPQL 빌더 역할
  • JPA 공식 기능
  • 너무 복잡하고 실용성이 없다.(실무에선 사용하지 않는다.)
  • Criteria 대신 QueryDSL 사용을 권장한다.

 

QueryDSL 소개

//JPQL
//select m from Member m where m.age > 18
JPAFactoryQuery queryFactory= new JPAQueryFactory(em); 
QMember m = QMember.member;
List<Member> list = queryFactory.selectFrom(m) 
                                .where(m.age.gt(18))
                                .orderBy(m.name.desc())
                                .fetch()
  • 문자가 아닌 자바코드로 JPQL을 작성할 수 있음
  • JPQL 빌더 역할
  • 컴파일 시점에 문법 오류를 찾을 수 있음
  • 동적쿼리 작성 편리
  • 단순하고 쉬움
  • 실무 사용 권장

 

네이티브 SQL 소개

  • JPA가 제공하는 SQL을 직접 사용하는 기능
  • JPQL로 해결할 수 없는 특정 데이터베이스에 의존적인 기능
    ex. 오라클 CONNECT BY, 특정 DB만 사용하는 SQL 힌트
String sql ="SELECT ID, AGE, TEAM_ID, NAME FROM MEMBER WHERE NAME = 'kim'";
List<Member> resultList = em.createNativeQuery(sql, Member.class)
                            .getResultList();

 

JDBC 직접 사용, SpringJdbcTemplate 등

JPA를 사용하면서 JDBC 커넥션을 직접 사용하거나, 스프링 JdbcTemplate, Mybatis 등 함께 사용가능

단, 영속성 컨텍스트를 적절한 시점에 강제로 플러시 필요.

(ex: JPA를 우회해서 SQL을 실행하기 직전 영속성 컨텍스트를 수동 플러시 해줘야 한다.)

 

 

📌 기본 문법과 쿼리 API

JPQL 소개

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

@Entity
@Getter
@Setter
@ToString(exclude = "team")
@Table(name = "member")
public class Member {
    @Id
    @GeneratedValue
    private Long id;
    private String username;
    private int age;
    @ManyToOne
    @JoinColumn(name = "team_id")
    private Team team;

}

@Entity
@Getter
@Setter
@Table(name = "team")
public class Team {
    @Id @GeneratedValue
    private Long id;
    public String name;
    @OneToMany(mappedBy = "team", fetch = FetchType.LAZY)
    private List<Member> members = new ArrayList<>();

}

@Entity
@Getter
@Setter
@Table(name = "orders")
public class Order {
    @Id
    @GeneratedValue
    private Long id;
    public int orderAmount;
    @Embedded
    private Address address;
    @ManyToOne
    @JoinColumn(name = "product_id")
    private Product product;
}

@Entity
@Getter
@Setter
@Table(name = "product")
public class Product {
    @Id
    @GeneratedValue
    private Long id;
    private String name;
    private int price;
    private int stockAmount;
}

@Embeddable
@Getter
@Setter
public class Address {
    private String city;
    private String street;
    private String zipcode;
}

 

JPQL 문법

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

 

집합과 정렬

  • count(m), sum(m.age), avg(m.age), max(m.age), min(m.age)
  • group by, having
  • order by

 

 

TypeQuery, Query

  • TypeQuery : 반환 타입이 명확할 때 사용
TypedQuery<Member> query = em.createQuery("SELECT m FROM Meber m", Member.class);
  • Query : 반환 타입이 명확하지 않을 때 사용
Query query = em.createQuery("SELECT m.username, m.age FROM Meber m"); // m.username : Sring, m.age : int

 

 

결과 조회 API

  • query.getResultList() : 결과가 하나 이상일 때, 리스트 반환
    • 결과가 없으면 빈 리스트 반환
List<Member> resultList = em.createQuery("select m from Member m", Member.class).getResultList();
  • query.getSingleResult(): 결과가 정확히 하나, 단일 객체 반환
    • 결과가 없으면: javax.persistence.NoResultException
    • 둘 이상이면: javax.persistence.NonUniqueResultException
Member singleResult = em.createQuery("select m from Member m", Member.class).getSingleResult();

 

 

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

위치 기준 파라미터 바인딩은 순서가 변경되면 수정이 필요하기 때문에 추천하지 않는다.

// 파라미터 이름 기준
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);

 

 

 


해당 글은 인프런의 [자바 ORM 표준 JPA 프로그래밍 - 기본편] 강의를 정리한 내용입니다.

 

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

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

www.inflearn.com