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

노력하는 배짱이·2022년 8월 10일
0

JPA

목록 보기
9/9

소개

JPQL

  • JPA는 SQL을 추상화한 JPQL이라는 객체 지향 쿼리 언어 제공
  • SQL과 문법 유사
    -> SELECT, FROM 등
  • 테이블이 아닌 객체를 대상으로 검색하는 객체 지향 쿼리
val result = em.createQuery("select m from Member as m where m.username like '%kim%'", Member::class.java).resultList

Criteria

  • 문자가 아닌 자바 코드로 JPQL을 작성할 수 있음
  • JPQL 빌더 역할
  • 너무 복잡하고 실용성이 없는 단점 존재
    -> QueryDSL 사용 권장
// Criteria 사용 준비
val cb: CriteriaBuilder = em.criteriaBuilder
val query: CriteriaQuery<Member> = cb.createQuery(Member::class.java)

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

// 쿼리 생성
val cq: CriteriaQuery<Member> = query.select(m).where(cb.equal(m.get<String>("username"), "kim"))
        
// 결과
val result = em.createQuery(cq).resultList

QueryDSL

  • 문자가 아닌 자바코드로 JPQL을 작성할 수 있음
  • JPQL 빌더 역할
  • 컴파일 시점에 문법 오류 찾을 수 있음
  • 동적쿼리 작성 편리

네이티브 SQL

  • JPA 제공하는 SQL을 직접 사용하는 기능
  • JPQL로 해결할 수 없는 특정 데이터베이스에 의존적인 기능

JDBC 직접 사용, SpringJdbcTemplate 등

  • JPA를 사용하면서 JDBC 커넥션을 직접 사용하거나, Spring JdbcTemplate, 마이바티스 등을 함께 사용 가능
  • 단 영속성 컨텍스트를 적절한 시점에 강제로 플러시 필요

기본 문법과 쿼리 API

기본

  • ex) select m from Member as m where m.age > 20

  • 엔티티와 속성은 대소문자 구분 해야함
    -> Member , age

  • JPQL 키워드는 대소문자 구분 할 필요 없음
    -> select, from, where

  • 테이블 이름이 아닌 엔티티 이름 사용 -> Member

  • 별칭은 필수 -> m
    -> as 키워드는 생략 가능

문법

기본적으로 제공하는 문법은 사용 가능

  1. select, from, where, groupby, having, orderby

  2. count, sum, avg, max, min

TypeQuery, Query

  1. TypeQuery : 반환 타입이 명확할 때 사용
  2. Query : 반환 타입이 명확하지 않을 때 사용
val typedQuery: TypedQuery<Member> = em.createQuery("select m from Member m", Member::class.java)

val query: Query = em.createQuery("select m.username, m.age from Member m")

결과 조회

  1. getResultList() : 결과가 하나 이상일 때 리스트를 반환
    -> 결과가 없으면 빈 리스트

  2. getSingleResult() : 결과가 정확히 하나일 때 단일 객체 반환
    -> 결과가 없거나 2개 이상일 경우 예외 발생

// 하나 이상의 결과
val resultList = query.resultList

// 하나 결과
query.singleResult

파라미터 바인딩

  1. 이름 기준
    -> 파라미터를 넣을 곳 앞에 :(콜론) 을 넣어야 함
val q: TypedQuery<Member> = em.createQuery("select m from Member m where m.username = :username", Member::class.java)
q.setParameter("username", "member1")
  1. 위치 기준
    -> 파라미터 넣을 곳 앞에 ?(물음표) 를 넣어야 함
val q = em.createQuery("select m from Member m where m.username = ?1", Member::class.java)
q.setParameter(1, "member1")

프로젝션 (SELECT)

  • select 절에 조회할 대상을 지정하는 것
  • 대상 : 엔티티, 임베디드 타입, 스칼라 타입(tntwk, answk emd)
  1. 엔티티 프로젝션
    -> em.createQuery("select m from Member m", Member::class.java)
    -> em.createQuery("select m.team from Member m", Team::class.java)

  2. 임베디드 타입 프로젝션
    -> Address 는 임베디드 타입
    -> em.createQuery("select o.address from Order o", Order::class.java)

  3. 스칼라 타입 프록젝션
    -> em.createQuery("select m.username, m.age from Member m")

여러 값 조회

  • ex) em.createQuery("select m.username, m.age from Member m")
  1. Query 타입으로 조회
  2. Query[] 타입으로 조회
  3. new 명령어 조회
    -> 단순 값을 DTO로 바로 조회
    -> 패키지 명을 포함한 전체 클래스 명 입력
    -> 순서와 타입이 일치하는 생성자 필요

페이징

  • setFirstResult(startPosition: Int) : 조회 시작 위치

  • setMaxResults(maxResult: Int) : 조회할 데이터 수

val result = em.createQuery("select m from Member m order by m.age desc ", Member::class.java)
		.setFirstResult(1)
        .setMaxResults(10)
        .resultList

조인

  1. 내부 조인 : select m from Member m [INNER] join m.team t

  2. 외부 조인 : select m from Member m left [OUTER] join m.team t

  3. 세타 조인 : select count(m) from Member m Team t where m.username = t.name

ON 절

  1. 조인 대상 필터링
    ex) 회원과 팀을 조인하면서, 팀 이름이 A인 팀만 조인
    JPQL : select m, t from Member m left join m.team t on t.name = 'A'
    SQL : select m., t. from Member m left join Team t on m.TEAM_ID = t.id and t.name = 'A'
  1. 연관관계 없는 엔티티 외부 조인
    ex) 회원의 이름과 팀의 이름이 같은 대상 외부 조인
    JPQL : select m, t from Member m left join Team t on m.username = t.name
    SQL : select m., t. from Member m left join Team t on m.username = t.name

서브 쿼리

ex) 나이가 평균보다 많은 회원
-> select m from Member m where m.age (select avg(m2.age) from Member m2)

지원 함수

  1. [NOT] Exists (subquery) : 서브쿼리에 결과가 존재하면 참
    -> em.createQuery("select m from Member m where exists (select t from m.team t where t.name = 'teamA')")

  2. {ALL | ANY | SOME } (subquery)
    -> ALL : 모두 만족하면 참
    -> ANY, SOME : 조건을 하나라도 만족하면 참

  3. [NOT] IN (subquery) : 서브쿼리의 결과 중 하나라도 같은 것이 있으면 참

한계

  • JPA는 WHERE, HAVING 절에서만 서브 쿼리 사용 가능
    -> 하이버네이트에서는 SELECT 절도 가능

  • FROM 절의 서브 쿼리는 JPQL에서 불가능
    -> 조인으로 풀 수 있으면 풀어서 해결

JPQL 타입 표현과 기타 식

타입 표현

  • 문자 : ‘HELLO’, ‘She’’s’

  • 숫자: 10L(Long), 10D(Double), 10F(Float)

  • Boolean: TRUE, FALSE

  • ENUM: xxx.Type.Admin (패키지명 포함)
    -> em.createQuery("select m from Member m where m.type = xxx.Type.Admin")

  • 엔티티 타입: TYPE(m) = Member (상속 관계에서 사용)

기타

SQL과 문법이 같은 식

  • EXISTS, IN
  • AND, OR, NOT
  • =, >, >=, <, <=, <>
  • BETWEEN, LIKE, IS NULL

조건식(CASE 등)

CASE 식

  1. 기본 CASE 식
em.createQuery(
	"select " +
	"case when m.age <= 10 then '학생요금'" +
	"when m.age >= 60 then '경로요금'" +
	"else '일반요금'" +
	"end " +
	"from Member m"
)
  1. 단순 CASE 식
em.createQuery(
	"select " +
	"case t.name " +
	"when '팀A' then '인센티브110%'" +
	"when '팀B' then '인센티브120%'" +
	"else '인센티브105%'" +
	"end " +
	"from Team t"
)
  1. COALESCE : 하나씩 조회해서 null이 아니면 반환
em.createQuery("select coalesce(m.username, '이름 없는 회원') from Member m ")
  1. NULLIF : 두 값이 같으면 null 반환, 다르면 첫번째 값 반환
em.createQuery("select nullif(m.username, '관리자') from Member m ")

JPQL 함수

  1. CONCAT

  2. SUBSTRING

  3. TRIM

  4. LOWER, UPPER

  5. LENGTH

  6. LOCATE

  7. ABS, SQRT, MOD

  8. SIZE, INDEX(JPA 용도)

  9. 사용자 정의 함수 호출
    -> 하이버네이트는 사용 전 방언에 추가해야 함
    -> 사용하는 DB 방언을 상속받고, 사용자 정의 함수를 등록
    -> 커스텀한 DB 방언 등록 (persistence.xml)

class MyH2Dialect() : H2Dialect() {
    override fun registerFunction(name: String?, function: SQLFunction?) {
        super.registerFunction("group_concat", StandardSQLFunction("group_concat", StandardBasicTypes.STRING))
        super.registerFunction(name, function)
    }
}

// main.kt
val result = em.createQuery("select function('group_concat', m.username) from Member m ", String::class.java).resultList

참고 : 인프런 강의[자바 ORM 표준 JPA 프로그래밍 - 기본편]

0개의 댓글