특정 데이터베이스 기능이나 구문을 직접 사용해야 할 필요가 있는데 이러한 경우, Spring Data JPA에서는 네이티브 SQL 쿼리를 작성하고 실행할수 있는 기능을 지원한다.
네이티브 쿼리의 필요성은 다음과 같다.
1. 특정 데이터베이스 기능 사용
2. 복잡한 쿼리 실행
3. 기존 SQL 쿼리 통합
@Query 어노테이션내에서 nativeQuery 속성을 true로 설정하고, SQL 쿼리를 직접 작성
사용 예제
✍️ 작성
package org.example.springdatajpa5;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
public interface UserRepository extends JpaRepository<User, Long> {
// 네이티브 SQL을 사용한 사용자 조회 (이메일에 특정 문자열 포함)
@Query(value = "SELECT * FROM jpa_user WHERE email LIKE %?1%", nativeQuery = true)
List<User> findByEmailNative(String email);
// 나이가 30 초과이고 상태가 'ACTIVE'인 사용자 수 조회
@Query(value = "SELECT COUNT(*) FROM jpa_user WHERE age > 30 AND status = 'ACTIVE'", nativeQuery = true)
int countActiveUsersOlderThan30();
// 네이티브 쿼리를 사용하여 이름에 특정 문자열이 포함된 사용자들의 name, email 조회
// 결과는 Object[] 배열의 List로 반환됨
@Query(value = "SELECT name, email FROM jpa_user WHERE name LIKE %:name%", nativeQuery = true)
List<Object[]> findUsersByNameNative(@Param("name") String name);
}
Criteria API는 JPA의 한 부분으로, SQL이나 JPQL 문자열을 직접 작성하지 않고도 자바 코드로 쿼리를 만드는 방법이다. 즉, 코드 내에서 타입에 안전한 방식으로 쿼리를 구성할 수 있어서, 쿼리를 작성할 때 실수를 줄이고, 조건에 따라 동적으로 쿼리를 생성할 수 있게 해준다. 개발자가 마치 SQL을 다루듯이 자바 코드로 쿼리 연산을 수행할 수 있게 해주는 객체지향 API라고 보면 된다.
CriteriaBuilder
CriteriaQuery
Root
Criteria API로 동적 쿼리를 생성하는 기본 절차를 아래와 같이 정리할 수 있음
1. CriteriaBuilder 인스턴스 생성
2. CriteriaQuery 객체 생성
3. Root 정의
4. 조건 추가 (Where 절)
5. 쿼리 실행
✍️ 작성
package com.example.springdatajpa3;
import jakarta.persistence.EntityManager;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import lombok.RequiredArgsConstructor;
import java.util.ArrayList;
import java.util.List;
@RequiredArgsConstructor
public class UserRepositoryCustomImpl implements UserRepositoryCustom {
// 엔티티 매니저를 통해 DB와의 연결을 관리함.
private final EntityManager entityManager;
@Override
public List<User> findUsersByName(String name) {
// CriteriaBuilder: Criteria 쿼리를 만들기 위한 팩토리 객체. 다양한 조건과 표현식을 생성할 수 있음.
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
// CriteriaQuery: 반환할 결과 타입(User)을 지정하며, 쿼리 구조(SELECT, WHERE 등)를 정의함.
CriteriaQuery<User> query = criteriaBuilder.createQuery(User.class);
// Root: 쿼리의 FROM 절에 해당하는 부분. 여기서는 User 엔티티가 기준이 됨.
Root<User> user = query.from(User.class);
// SELECT 절에서 user를 선택하고, WHERE 조건으로 name 필드가 포함된 값을 찾도록 지정.
// criteriaBuilder.like()를 사용해서 SQL의 LIKE 연산자와 동일한 효과를 냄.
query.select(user)
.where(criteriaBuilder.like(user.get("name"), "%" + name + "%"));
// 완성된 CriteriaQuery를 기반으로 실제 쿼리를 실행해 결과를 가져옴.
return entityManager.createQuery(query).getResultList();
}
@Override
public List<User> findusersDynamically(String name, String email) {
// CriteriaBuilder를 사용하여 쿼리 생성 준비.
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<User> query = cb.createQuery(User.class);
Root<User> user = query.from(User.class);
// 동적으로 WHERE 조건을 추가하기 위해 Predicate 리스트를 생성.
List<Predicate> predicates = new ArrayList<>();
// name 파라미터가 null이 아니라면, name 조건을 추가.
if(name != null) {
predicates.add(cb.equal(user.get("name"), name));
}
// email 파라미터가 null이 아니라면, email 조건을 추가.
if(email != null) {
predicates.add(cb.equal(user.get("email"), email));
}
// 리스트에 담긴 모든 조건들을 AND 연산자로 결합하여 WHERE 절에 설정.
query.select(user)
.where(cb.and(predicates.toArray(new Predicate[0])));
// 위 주석처럼 사용자가 입력한 값에 따라 조건이 동적으로 생성됨.
// 예시:
// - name = null, email = null → 전체 User 조회 (WHERE 조건 없음)
// - name != null, email = null → name 조건만 적용
// - name = null, email != null → email 조건만 적용
// - name != null, email != null → 두 조건 모두 적용
// 실제 쿼리를 실행하고 결과 리스트를 반환.
return entityManager.createQuery(query).getResultList();
}
}
해당 코드에서는 CriteriaBuilder로 조건을 생성하고,
CriteriaQuery로 쿼리의 전체 구조를 잡으며,
Root로 기준 엔티티를 지정함.
동적으로 조건을 추가할 수 있는 예제로 필요에 따라 추가 조건을 유연하게 설정할 수 있음