본문 바로가기
IT/Spring & Spring Boot

[Spring Boot] 네이티브 쿼리 vs JPQL

by 저당단 2025. 12. 24.

네이티브 쿼리

@Query(
  value = "SELECT * FROM users WHERE age >= 20", // 일반적인 SQL
  nativeQuery = true
)
List<User> findAdults();
  • DB에 직접 SQL 날림
  • JPA와 별개로 동작
  • 결과를 객체로 매핑만 해줌

JPA 생태계와 독립적으로 날리는 쿼리이다.

즉 DB 종속적이므로 컬럼명이나 테이블명이 변경되면 유지보수가 번거로울 수 있다.

 

 

JPQL(Java Persistence Query Language)

일반적인 SQL과 문법이 거의 비슷하지만 살짝 다르다.

@Query(
  value = "SELECT * FROM User WHERE age >= 20", // User는 테이블명이 아닌 엔티티명. 대소문자 구별함
)
List<User> findAdults();
  • 네이티브 쿼리와는 달리 엔티티 객체를 조회
  • 실행 시점에 JPA가 JPQL을 실제 SQL로 변환시킴
  • 잘못 쓰면 성능은 네이티브에 비해 느릴 수 있음

여기서 말하는 잘못 쓴 경우는 User에 연관된 엔티티가 있을 때,

연관 객체를 무조건 사용하는데도 FetchType.Lazy를 사용하는 경우 등을 말한다.

잘못된 JPA 설계 문제만 없으면 JPQL은 네이티브 쿼리와 성능이 크게 차이나지 않는다.

 

하지만 복잡한 쿼리를 필요로 할 땐 이런 정책까지 신경쓰기 힘들 수 있는데,

이럴 때 네이티브 쿼리를 사용하면 최대 성능을 뽑아낼 수 있으니 전략적으로 판단해야 한다.

 

 

JPA Criteria

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<User> cq = cb.createQuery(User.class);

Root<User> user = cq.from(User.class);

cq.select(user)
  .where(cb.greaterThanOrEqualTo(user.get("age"), 20));

List<User> result = em.createQuery(cq).getResultList();

검색 필터 등 동적인 조건을 필요로 할 때 JPQL을 만들어주는 API이다.

참고로 JPQL 예시에 있던 쿼리랑 완전히 같은 쿼리이다.

이와 같이 가독성은 별로 좋지 않으므로 동적 조건이 아니면 사실상 쓸 이유가 거의 없다.

 

유의해야 될 점은

@Query("SELECT u FROM User u")
List<User> findAll(Specification<User> spec);

이 코드는 안 된다는 점이다.

@Query에서도 JPQL을, Specification에서도 JPQL을 만들고 있는데,

이러면 무조건 @Query쪽만 실행되고 Specification은 무시된다.

Specification을 쓴 이유는 동적 쿼리 때문일 테니 그쪽으로 통일시키는 것이 적절한 방법이다.

 

이게 사실상 이 포스트를 쓴 이유다...