*본 포스트는 김영한님의 “자바 ORM 표준 JPA 프로그래밍 - 기본편” 을 수강하며 학습을 기록하는 내용입니다.

소개

다양한 쿼리 방법 지원


JPA Criteria

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


JPQL

대부분의 경우 JPQL로 해결됨


EntityManager.find()

객체 그래프 탐색

  • 나이가 18살 이상인 회원을 모두 조회하고 싶다면?
  • JPA는 엔티티 객체를 중심으로 개발해야 함.
  • 검색 시에도 테이블이 아닌 객체로 변환해서 검색하는 것은 불가능하다.
  • 데이터에서 어플리케이션에 올릴 데이터를 가져올 때는 성능을 위해 검색 조건이 포함된 SQL이 필요하다.


JPA는 SQL을 추상화한 JPQL이라는 객체 지향 쿼리 언어를 제공하고.. JQPL은 엔티티 객체를 대상으로 쿼리를 날린다는 점이 SQL은 데이터베이스 테이블을 대상으로 쿼리를 날린다는 점과 다르다.


Criteria 사용하면 컴파일 시 오류를 띄워주는 부분과 동적 쿼리 사용 시에 장점이 있다.

  • 그런데 유지보수 측면에서 안좋아서 실무에서 사용을 잘 안한다.(코드를 알아보기가 쉽지 않기 때문)


Criteria 대신에 QueryDSL(오픈소스 라이브러리) 사용

  • QueryDSL은 자바 코드로 JPQL 작성할 수 있다는 점, JPQL 빌더 역할, 동적쿼리 작성 편리, 단순하고 쉬운 점 때문에 실무에 사용을 권장한다.

(QueryDSL은 JPA 문법 강의에서는 다루지 않음 - 다른 강의를 이후 수강하거나 공식 사이트의 reference 참조)


네이티브 SQL - JPA가 제공하는 SQL을 직접 사용하는 기능, JPQL로 해결할 수 없는 특정 DB에 의존적인 기능 (ex, 오라클의 connect by 등..)

// java
// 네이티브 SQL 사용법
em.createNativeQuery(sqlString).getResultList();
  • 보통 근데 실무에서는 JDBC 직접 사용하거나 SpringJdbcTemplate, myBatis 등을 사용함
  • 단, 영속성 컨텍스트를 적절한 시점에 강제로 플러시 필요하다.

(ex. JPA를 이용해서 쿼리를 날리는 경우면 자동으로 flush 되서 상관없지만, dbconn 같은 경우는 안되므로 강제 flush가 필요하다)


프로젝션

⇒ SELECT 절에 조회할 대상을 지정하는 것이다.

  • 엔티티 프로젝션
  • 임베디드 타입 프로젝션
  • 스칼라 타입 프로젝션
  • 엔티티 프로젝션은 영속성 컨텍스트에서 관리가 된다.


“select m.team from Member m” 이라는 JPQL을 날리면 날아가는 실제 쿼리는 JOIN 문이 날아간다.(SQL에서는 Member로 연관지어서 TEAM 테이블에서 데이터를 가져오므로)

⇒ “select t from Member m join m.team t” 이렇게 써야 한다. 명시적으로 작성해서 SQL을 예측할 수 있어야 함.

여러 값 조회

“SELECT m.username, m.age FROM Member m”

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

페이징 API

  • setFirstResult(int startPosition) : 조회 시작 위치(0부터 시작)
  • setMaxResult(int endPosition)

각 DBMS 마다 복잡한 방언을 이 두가지만 설정하면 해결해준다.

서브쿼리

  • JPA 표준에서는 where, Having 절에서만 서브쿼리가 사용가능하다.
  • 최신 하이버네이트에서는 Select 절에서도 사용 가능하다.
  • FROM 절에서는 서브쿼리를 지원하지 않는다.
    • 최대한 조인으로 이를 해결하자.

조건문

  • SELECT coalesce(m.username, “이름 없는 회원”) : m.username이 없으면 “이름 없는 회원”으로 반환
  • SELECT NULLIF(m.username, “관리자”) : 회원이름이 ‘관리자’ 면 null을 반환, 나머지는 username 반환

JPQL 기본 함수

  • CONCAT - 문자열을 더함(+)
  • SUBSTRING - 문자열 잘라내기 (ex. “SELECT substring(m.username, 2, 3)”)
  • TRIM
  • LOWER, UPPER
  • LENGTH
  • LOCATE - 찾으려는 문자의 인덱스를 찾아서 반환해줌
  • ABS, SQRT, MOD
  • SIZE, INDEX (JPA 용도) : size()는 컬렉션의 크기를 돌려줌. index()는 @OrderColumn 사용할 때 값타입 컬렉션에서 사용할 수 있는건데, 컬렉션의 위치값을 구할 때 쓸 수 있음(웬만하면 안쓰는게 좋다. 추천하지 않음)

사용자 정의 함수 호출

  • 사용하는 DB 방언을 상속 받고, 사용자 정의 함수를 정의해야 함
  • 정의한 후, 의존성을 상속해서 정의한 클래스로 바꿔준다.