본문 바로가기

Spring/Spring JPA

Native Query

Native Query는 JPA에서 제공하는 기능 중 하나로, JPQL 대신 SQL 쿼리를 직접 작성해서 사용할 수 있는 방법입니다. Native Query는 데이터베이스에 종속적이며, 특정 데이터베이스에서만 동작할 수 있는 SQL 구문을 사용할 수 있습니다.

JPA의 추상화된 JPQL을 사용하는 대신, 더 강력하고 세밀한 제어가 필요한 경우 Native Query를 사용합니다.

 

Native Query의 특징

  1. SQL 기반
    • JPQL과 달리, 데이터베이스 테이블 및 컬럼 이름을 직접 사용하여 쿼리를 작성합니다.
  2. 데이터베이스 종속성
    • 특정 데이터베이스의 고유 기능(예: MySQL의 LIMIT, PostgreSQL의 JSONB 등)을 사용할 수 있습니다. 따라서 데이터베이스 변경 시 쿼리도 수정이 필요할 수 있습니다.
  3. 복잡한 쿼리 처리
    • JPQL로 처리하기 어려운 복잡한 쿼리, 다중 조인, 서브쿼리, 또는 데이터베이스 특화 기능을 사용할 때 유용합니다.
  4. 결과 매핑
    • Native Query의 결과는 JPA 엔티티에 매핑하거나, DTO(Data Transfer Object), 또는 맵 형식으로 받을 수 있습니다.
  5. 퍼포먼스 고려
    • Native Query는 엔티티 매핑과는 별개로 SQL로 직접 작업하기 때문에 성능 최적화가 가능합니다.

 

Native Query 사용 방법

1. 기본 사용

@Query 어노테이션에 nativeQuery = true를 설정하면 Native Query를 사용할 수 있습니다.

@Repository
public interface UserRepository extends JpaRepository<User, Long> {

    @Query(value = "SELECT * FROM users WHERE age > :age", nativeQuery = true)
    List<User> findUsersOlderThan(@Param("age") int age);
}

 

2. DTO로 결과 매핑

Native Query의 결과를 특정 DTO로 받을 수 있습니다.

public interface UserRepository extends JpaRepository<User, Long> {

    @Query(value = "SELECT u.id AS id, u.name AS name, u.email AS email FROM users u WHERE u.age > :age", nativeQuery = true)
    List<UserDTO> findUserDTOs(@Param("age") int age);

    interface UserDTO {
        Long getId();
        String getName();
        String getEmail();
    }
}

 

3. 엔티티로 결과 매핑

Native Query로 반환된 데이터를 엔티티에 매핑할 수도 있습니다.

@Query(value = "SELECT * FROM users WHERE age > :age", nativeQuery = true)
List<User> findUsersByAge(@Param("age") int age);

 

4. ResultSet으로 매핑

결과를 특정 클래스로 매핑하지 않고, List<Object[]> 또는 Map 형태로 받을 수도 있습니다.

@Query(value = "SELECT name, email FROM users WHERE age > :age", nativeQuery = true)
List<Object[]> findNameAndEmailByAge(@Param("age") int age);

 

Native Query와 JPQL의 차이

특징 JPQL Native Query
쿼리 언어 객체 중심의 쿼리 언어 (엔티티와 필드를 대상으로 함) SQL 언어 (테이블과 컬럼을 대상으로 함)
데이터베이스 독립성 데이터베이스 독립적 데이터베이스 종속적
사용 목적 기본적인 CRUD 및 엔티티 간 연관 관계 처리 복잡한 쿼리나 특정 데이터베이스 특화 기능 처리
결과 매핑 엔티티 매핑이 기본 엔티티, DTO, 또는 객체 배열로 매핑 가능

 

Native Query의 장점

  1. 복잡한 쿼리 처리
    • JPQL로 처리하기 어려운 복잡한 서브쿼리, 집계 함수, 윈도우 함수 등을 사용할 수 있습니다.
  2. 데이터베이스 특화 기능 활용
    • 예를 들어, PostgreSQL의 JSONB, MySQL의 FULLTEXT 검색, Oracle의 CONNECT BY 등을 활용 가능.
  3. 성능 최적화
    • 데이터베이스에 최적화된 쿼리를 작성하여 퍼포먼스를 극대화할 수 있습니다.
  4. 직접 제어 가능
    • 엔티티 매핑이 필요 없는 경우에도 데이터를 효율적으로 조회할 수 있습니다.

Native Query의 단점

  1. 데이터베이스 종속성
    • 데이터베이스에 특화된 SQL을 사용할 경우, 다른 데이터베이스로 이식하기 어렵습니다.
  2. 유지보수 어려움
    • SQL 쿼리가 코드 내부에 하드코딩되므로, 변경 시 유지보수가 어렵고, 추상화 수준이 낮아집니다.
  3. 엔티티 매핑 한계
    • 쿼리 결과가 JPA 엔티티와 잘 매핑되지 않을 경우, 추가적인 코드 작업이 필요합니다.
  4. 가독성 저하
    • 복잡한 Native Query는 코드의 가독성을 해칠 수 있습니다.

 

Native Query 예제

1. 복잡한 JOIN과 서브쿼리

@Query(value = "SELECT u.name, COUNT(o.id) AS order_count " +
               "FROM users u " +
               "JOIN orders o ON u.id = o.user_id " +
               "WHERE u.age > :age " +
               "GROUP BY u.name " +
               "HAVING COUNT(o.id) > 2", nativeQuery = true)
List<Object[]> findUserWithOrderCount(@Param("age") int age);

 

2. 데이터베이스 특화 기능 활용 (MySQL 예제)

@Query(value = "SELECT * FROM users WHERE MATCH(name, email) AGAINST(:keyword IN NATURAL LANGUAGE MODE)", nativeQuery = true)
List<User> searchUsersByFullText(@Param("keyword") String keyword);

 

3. DTO 매핑 (사용자 정의 결과 매핑)

@Query(value = "SELECT u.id, u.name, COUNT(o.id) AS order_count " +
               "FROM users u " +
               "LEFT JOIN orders o ON u.id = o.user_id " +
               "GROUP BY u.id, u.name", nativeQuery = true)
List<UserOrderDTO> findUserOrderStats();

 

 

Native Query를 사용할 때 주의할 점

  1. SQL Injection 방지
    • @Param과 같은 매개변수 바인딩을 반드시 사용하여 직접 문자열로 쿼리를 조합하지 말아야 합니다.
  2. 데이터베이스 변경 가능성 고려
    • 데이터베이스가 변경될 가능성이 있는 프로젝트에서는 Native Query 사용을 최소화하는 것이 좋습니다.
  3. 테스트와 검증
    • Native Query는 데이터베이스에 직접 의존하므로, 실행 전에 철저히 테스트해야 합니다.
  4. 복잡성 최소화
    • 유지보수를 위해 쿼리를 간단하게 작성하려고 노력하고, 필요하면 JPQL로 대체합니다.

Native Query는 복잡한 요구 사항이나 성능 최적화가 필요한 경우 강력한 도구가 될 수 있습니다. 하지만 JPQL을 먼저 시도하고, 그것으로 충분하지 않을 때 Native Query를 사용하는 것이 좋습니다

'Spring > Spring JPA' 카테고리의 다른 글

@Embedded, @Embeddable 간단하게  (0) 2025.01.07
@Converter 간단하게  (1) 2025.01.07
JPQL 간단히  (0) 2025.01.06
FetchType 간단히  (0) 2024.12.27
Cascade(영속성 전이)  (0) 2024.12.27