JPA 쿼리 방법

JPA에서 쿼리를 실행하는 방법에는 4가지가 있다.

가장 중요한 JPQL과 QueryDSL이 있고, Criteria, 네이티브 쿼리 기능도 존재한다.

 

JPQL

테이블 기준이 아닌 엔티티를 기준으로 쿼리를 작성하며, 실행 시점에 DB쿼리로 변환된다.

JPQL은 실행 시점에 1차 캐시를 조회하지 않고 바로 플러시되는 특징이 있고, 조회된 객체는 1차 캐시에 저장되어 영속된다.

엔티티는 객체이기 때문에 선언을 해줘야 컨트롤 할 수 있다. 따라서 m이라는 별칭을 꼭 써주어야한다.

SELECT m FROM Member m WHERE m.id = 1;

 

 

패치 조인

JPA에서 성능 최적화를 위해 제공하는 기능으로 연관된 엔티티나 컬렉션을 한번의 SQL로 함께 조회가 가능하다.

 

일반 조인을 사용할 경우 지연 로딩프록시에 의해 조인된 테이블의 ID만 가져오고, 실제 사용 시점에 다시 쿼리를 발생 시키게되어 N + 1 문제를 해결할 수 없다.

이런 상황에서 JOIN FETCH를 사용하게 되면 즉시 로딩처럼 연관 객체의 데이터를 한번에 다 가져올 수 있다.

SELECT m FROM Member m JOIN FETCH m.team;


패치 조인의 대상에는 별칭을 줄 수 없기 때문에 쿼리에서 조인 대상의 필드를 전부 조회해야한다는 단점이 있다.

여러 테이블을 조인해서 필요한 필드만 가져오고싶다면, 일반 조인을 통해 필요한 데이터만 조회해서 DTO로 반환하는 것이 좋다.


벌크 연산

N개의 데이터를 변경하는 쿼리가 있다고 할 때 지연 로딩으로 쿼리를 생성하면 N개의 업데이트 쿼리가 발생한다. 이때 벌크 연산을 사용하면 중복 쿼리를 하나의 쿼리로 만들어 최적화 할 수 있다.
JPQL에 대해 executeUpdate()를 호출하면 되고, JPA 스펙에서는 UPDATE와 DELETE만 지원하지만 하이버네이트에서는 INSERT도 지원한다.
단, 영속성 컨텍스트를 무시하고 DB에 쿼리를 수행하기 때문에 트랜잭션 커밋 시점에 쿼리가 꼬일 수 있다. 따라서 사용 후 반드시 영속성 컨텍스트를 비워줘야한다.

 

QueryDSL

JPQL은 문자열이기 때문에 동적 쿼리를 작성하기 힘들다는 단점이 있다.

조건에 따라 문자열을 추가하는 방식을 사용한다면 컴파일 시점에 오류를 찾을 수 없기 때문에 유지보수가 힘들다.

 

QueryDSL을 사용하면 자바 코드로 쿼리를 생성하기 때문에 동적 쿼리를  작성할 수 있고, 컴파일 타임에 문법 오류를 찾을 수도 있다.
QueryDSL은 JPQL 빌더 오픈소스 라이브러리이기 때문에 기본적으로 JPQL을 잘 다룰 수 있어야한다.

 

예시 코드

queryFactory.select(member)
    .from(member)
    .where(member.name.like(“kim”))
    .featch();

 

 

Criteria

Criteria는 QueryDSL과 같이 동적 쿼리를 자바 코드로 작성할 수 있지만, 너무 복잡하고 QueryDSL 보다 직관성이 떨어져서 무슨 쿼리인지 한번에 알아보기 힘들기 때문에 QueryDSL을 사용하자.

 

네이티브 쿼리

JPA에서는 네이티브 쿼리를 작성할 수 있다. 특정 DB에 종속적인 쿼리를 사용할 때 사용되지만, JPA와 JDBC Template, MyBatis를 같이 사용할 수 있기 때문에 Mapper를 쓰는 것이 더 편할 수 있다.
하지만 매퍼는 영속성 컨텍스트가 적용되지 않기 때문에 네이티브 쿼리 사용 시 반드시 강제로 flush()해서 JPA 쿼리도 같이 실행해야 안전하다.

'개발 > Spring' 카테고리의 다른 글

Spring Boot html 파일 위치  (0) 2024.02.02
Spring JSON 응답 시 boolean 타입의 is가 생략되는 문제  (0) 2024.02.01
JPA 값 타입  (0) 2024.01.28
JPA 프록시와 지연로딩  (1) 2024.01.28
JPA 영속성 전이와 고아 객체  (0) 2024.01.28