JPA
- 복잡한 검색 조건을 사용해서 엔티티 객체를 조회할 수 있는 다양한 쿼리 기술을 지원한다.
- 애플리케이션과 JDBC 사이에서 동작한다.
- JPA 구동 방식 : Persistence(설정 정보를 조회) -> EntityManagerFactory(생성) -> EntityManager(생성)
- 앤티티 매니저 팩토리는 하나만 생성해서 애플리케이션 전체에서 공유된다.
- 엔티티 매니저는 스레드 간에 공유가 되지 않는다.
- JPA의 모든 데이터 변경은 트랜잭션 안에서 실행된다.
그래서 왜 쓰는 걸까?
- 유사한 CRUD SQL 반복 작업에 많은 시간을 사용했다.
- 객체를 단순히 데이터 전달 목적으로 사용할 뿐, 객체 지향적이지 못함
- 그래서 객체와 테이블을 매핑시켜주는 ORM이 주목받기 시작했고, 자바 진영에서는 JPA라는 표준 스펙이 정의되었다.
- JPA를 구현한 여러 프레임워크가 있지만 Hibernate가 JPA를 주도하고 있기 때문에 JPA를 Hibernate라 생각하면 편하지 않을까 싶다.
- Hibernate는 SQL을 직접 사용하지 않고, 메소드 호출만으로 쿼리가 수행된다.
- 즉, SQL 반복 작업을 하지 않으므로 생산성이 매우 높아진다.
- 그래도 SQL은 알아야 한다.
- 그 이유는 수행한 쿼리를 보면서 의도한 대로 쿼리가 짜였는지, 성능이 어떠한지에 대한 모니터링이 필요하기 때문에 SQL은 반드시 알아야 한다.
하지만 JPA에서 제공하는 메소드 호출만으로 섬세한 쿼리 작성이 어렵다.
그 결과, JPQL이 탄생했다.
그래서 보통 단순한 기능은 JPA를 사용하고, 좀 복잡한 쿼리가 있을 때 JPQL이나 Querydsl 둘 중 하나를 사용하게 된다.
JPQL(Java Persistence Query Language)
- SQL과 비슷한 문법을 가진 객체 지향 쿼리이다.
- 테이블 검색이 아니라 객체를 검색하는 객체지향 쿼리
- JPA는 JPQL을 분석해서 SQL을 자동으로 생성해서 DB에서 쿼리를 만들어준다.
JPQL 과정
- JPQL을 실행하면 영속성 컨텍스트로 요청을 보낸다.
- 영속성 컨텍스트는 1차 캐시에 엔티티 존재 여부 상관없이 DB에 질의한다.
- DB 질의를 통해 조회된 데이터는 영속성 컨텍스트가 다시 받는다.
- 데이터를 전달받은 영속성 컨텍스트는 엔티티를 초기화하고 캐시에 저장한다.
- 캐시에 저장한 후 엔티티를 반환한다.
//JPA : EntityManager 객체의 find() 메소드를 호출할 수 있다.
em.find(Item.class, id);
--------------------------------------------------------
//JPQL : createQuery를 통해 사용할 수 있다.
em.createQuery(
"select i from Item i where i.id = :id", Item.class)
.getResultList();
--------------------------------------------------------
//파라미터 바인딩(이름, 순서 중에서 이름 사용 추천)
em.createQuery(
"select i from Item i where i.id = :id", Item.class)
.setParameter("id", itemId)
.getResultList();
--------------------------------------------------------
//페이징 API
em.createQuery(...)
.setFirstResult(5) //5부터 시작
.setMaxResults(20) //20개 출력
.getResultList();
JPQL의 형태를 보면 SQL과 비슷하지만 다른 점이 있다.
- 대소문자 구분
- 엔티티와 속성은 대소문자를 구분한다.
- 엔티티 이름과 속성은 대소문자를 구분한다.
- JPQL 키워드는 대소문자 구분 없이 사용 가능하다.(SELECT == select)
- 엔티티명
- FROM 절 뒤에 사용하는 것은 클래스명이 아니라 엔티티명이다.
- @Entity(name=xx) 혹은 클래스명과 동일하면 생략 가능
- 별칭
- SQL과 다르게 JPQL에서는 별칭이 필수적으로 명시해야 한다.
- 생략도 가능하다.
- from 절 뒤에 해당 엔티티 + 별칭 형태로 사용한다.
- 파라미터 바인딩
- 클래스에 정의된 프로퍼티 앞에 : 를 사용한다.
- LIKE와 같은 형태를 사용할 때 주의할 점
- JPQL에 직접 문자를 쓰면 SQL Injection을 당할 수 있다.
- JPA에서 파라미터만 다를 뿐 같은 쿼리로 인식하므로, JPQL을 SQL로 파싱한 결과를 재사용할 수 있다.
- 페이징 API
- setFirstResult(int startPosition) : 조회 시작 위치
- setMaxResults(int maxResult) : 조회할 데이터 수
- DTO 사용
- 데이터를 전송하기 위해서 엔티티 자체를 쓸 수 있지만 엔티티 자체를 사용하는 것은 바람직하지 않다.
- DTO를 사용하면 약간의 불편한 점이 있다.
- new 뒤에 해당하는 패키지명을 모두 작성해야 한다.
- 그래서 Querydsl을 사용하면 조금 더 편하게 사용할 수 있다.
- 조인
- INNER JOIN : INNER은 생략 가능
- LEFT OUTER JOIN : OUTER 생략 가능
- FETCH JOIN
- JPQL에서 성능 최적화를 위해 제공하는 기능
- 연관된 엔티티나 컬렉션을 한 번에 같이 조회(JPQL은 결과를 반환할 때 연관까지 고려하지 않음)
- SQL 호출 횟수를 줄여 성능 최적화
- 쿼리 시점에 조회하므로 지연 로딩이 발생하지 않음
- N + 1 문제를 해결하는 데 주로 사용되는 방법이다.
//INNER JOIN
em.createQuery(
"select o From Order o join o.member m", Order.class)
.getResultList();
// Order + Member 한 번에 출력
----------------------------------------------------------------
//LEFT JOIN
em.createQuery(
"SELECT m, t FROM Member m LEFT JOIN m.team t on t.name = 'A'", Item.class)
.getResultList();
// 팀 이름이 A인 팀만 조인해서 출력
----------------------------------------------------------------
//FETCH JOIN
em.createQuery(
"select o from Order o" +
" join fetch o.member m" +
" join fetch o.delivery d", Order.class)
.getResultList();
// Order + Member + Delivery 한 번에 출력
'백엔드 > JPA' 카테고리의 다른 글
[JPA] EntityManager와 EntityManagerFactory (0) | 2021.12.08 |
---|---|
Spring Data JPA(Repository 간단히 만들기) (0) | 2021.09.11 |
JPQL을 Querydsl로 교체해보기 (0) | 2021.09.11 |
JPA 엔티티 매핑하기(@Entity) (0) | 2021.09.11 |
JpaRepository의 주요 메소드를 이해해보자. (0) | 2021.09.09 |
최근댓글