JPA JPQL

이상민·2021년 12월 3일
1

JPA

목록 보기
8/8
post-thumbnail

1. JPA의 쿼리

JPA는 다양한 쿼리 방법을 지원한다
1. JPQL
2. JPA Criteria (JPQL 사용)
3. QueryDsl (JPQL 사용)
4. 네이티브 SQL
5. JDBC API

  • 쌩 SQL과 JDBC는 DB 벤더 종속적인 상황에 유용하다. 근데 대부분 JPQL로 해결 가능

1-1. JPQL

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

  • 검색 할 때도 테이블이 아닌 엔티티 객체를 대상으로 검색할 수 있어야한다

  • 모든 DB 데이터를 객체로 변환하는 것은 불가능

  • 결국 애플리케이션이 필요한 데이터만 불러오려면 검색 조건이 포함된 SQL이 필요함

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

  • JPQL은 엔티티를 대상으로 쿼리한다

// JPQL
SELECT m 
FROM Member m 
WHERE m.username LIKE '%kim'
  • 문법이 거의 sql과 동일하다

  • JPQL문도 결국 문자열이기 때문에 동적 쿼리가 어렵고, 실수하기 쉽다

1-2. JPA Criteria

  • 동적 쿼리 같은 문제들 때문에 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();

1-3. QueryDSL

  • 그래서 또 쓰는게 QueryDSL이다. 마치 JPQL 문법을 자바 코드로 옮긴것과 같아 JPQL을 알면 QueryDSL도 쉽다
JPAFactoryQuery query = new JPAQueryFactory(em);
QMember m = QMember.member;

List<Member> list = 
    query.selectFrom(m)
        .where(m.age.gt(18))
        .orderBy(m.name.desc())
        .fetch();

1-4. 네이티브 SQL

  • 이 모든게 안된다면 네이티브 SQL을 쓴다
String sql = "SELECT id, age, team_id, name FROM member WHERE nme = 'kim'";
List<Member> resultList = em.createNativeQuery(sql, Member.class).getResultList();

1-5. JDBC API

  • 쌩 SQL은 너무 관리하기 힘들다

  • JDBC를 사용하면 JPA(정확히는 영속성 컨텍스트)와 아무 상관 없음에 주의하자

  • 영속성 컨텍스트를 적절한 시점에 강제로 플러시 해줘야하지만, JDBC 커넥션이나 마이바티스등을 JPA와 쓸 수도 있다


2. JPQL 문법

Java Persistence-layer Query Language
JPQL은 엔티티를 대상으로 쿼리한다. 추상화된 쿼리 언어로 특정 DB SQL에 의존하지 않는다

SELECT m FROM Member as m where m.age > 18

SELECT
    COUNT(m),
    SUM(m.age)
    AVG(m.age),
    MAX(m.age),
    MIN(m.age)
FROM Member m
  • 엔티티와 속성은 대소문자 구분

  • JPQL 키워드는 대소문자 구분X

  • 테이블명이 아닌 엔티티명 사용

  • 별칭이 필수

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

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

  • query.getResultList() : 결과 값이 하나 이상일때 사용. 리스트 반환

  • query.getSingleResult() : 단일 객체 반환

    • 예외 발생 주의
    • 없으면 NoResultException
    • 둘 이상이면 NonUniqueResultException

2-1. 파라미터 바인딩

// 이름 기준
TypedQuery<Member> query = em.createQuert(SELECT m FROM Member m WHERE m.username=:username);
query.setParameter("username", usernameParam);

// 위치 기준
TypedQuery<Member> query = em.createQuert(SELECT m FROM Member m WHERE m.username=?1);
query.setParameter(1, usernameParam);
  • 위치기반은 헷갈리니 쓰지말자

2-2. 프로젝션

  • SELECT 절에 조회할 대상을 지정

  • 엔티티, 임베디드 타입, 스칼라 타입이 대상이 될 수 있다

SELECT m FROM Member m  // 엔티티 프로젝션
SELECT m.address FROM Member m  // 임베디드 프로젝션
SELECT m.username FROM Member m  // 스칼라 타입 프로젝션 
  • jpql을 통해 엔티티를 가져와도 영속성 컨텍스트에 의해 관리 된다

  • 여러 값을 조회할 수도 있다

2-3. 페이징 API

  • setFirstResultsetMaxResults API를 사용해 벤더에 종속적이지 않게 페이징을 구현할 수 있다
em.createQuery("SELECT m FROM Member m order by m.age desc", Member.class)
    .setFirstResult(0)  // page
    .setMaxResults(10)  // size 
    .getResultList();

2-4. 조인

  • 내부 조인
SELECT m FROM Member m JOIN m.team t
  • 외부 조인
SELECT m FROM Member m LEFT JOIN m.team t
  • 세타 조인
SELECT COUNT(m) FROM Member m, Team t WHERE m.username = t.name
  • 조인 대상 필터리
SELECT m, t FROM Member m LEFT JOIN m.team t on t.name = 'A'
  • 연관관계 없는 엔티티 외부 조인
SELECT m, t 
FROM Member m 
LEFT JOIN Team t on m.username = t.name

2-5. 서브쿼리

SELECT m
FROM Member m
WHERE m.age > (SELECT AVG(m2.age) FROM Member m2)

SELECT m
FROM Member m
WHERE EXISTS (SELECT t FROM m.team t WHERE t.name = '팀A')
  • 상위 쿼리의 객체를 서브쿼리에서 사용하지 않아야 성능이 좋다

  • EXISTS, ALL, ANY 같은 함수를 지원한다

  • JPA는 HWERE, HAVING 절에서만 서브 쿼리 사용 가능하다

  • 하이버네이트는 SELECT절도 지원한다

  • FROM 절의 서브쿼리는 아직 안된다

2-6. 타입 표현

  • 문자 : ``
  • 숫자 : 10L, 10D, 10F
  • Boolean : TRUE, FALSE
  • Enum : package.name.Enum.TYPE
  • 엔티티 타입 : TYPE(m) = Member

2-7. 조건식

  • case 식
SELECT
    case when ,.age <= 10 then '학생요금'
        else '일반요금'
    end
from Member m

SELECT 
    case t.name
        when '팀A' then '인센티브110%'
        else '인센티브105%'
    end
from Team t

하나씩 조회해서 null 아니면 반환
SELECT coalesce(m.username, '이름 없는 회원') from Member m

두 값이 같으면 null, 다르면 첫번쨰 값 반환 
SELECT NULLIF(m.username, '관리자') from Member m

2-8. jpql 기본함수

CONCAT

SUBSTRING

TRIM

LOWER, UPPER

LENGTH

LOCATE

ABS, SQRT, MOD

SIZE, INDEX

profile
편하게 읽기 좋은 단위의 포스트를 추구하는 개발자입니다

0개의 댓글