김영한 님의 "자바 ORM 표준 JPA 프로그래밍" 책을 정리한 포스팅 입니다.
1. Native SQL
- JPA는 JPQL이 아닌 DB 고유의 SQL 문법을 직접 사용하는 방법을 지원합니다.
- 결과 매핑 어노테이션을 사용하여 응답을 정의할 수 있습니다.
사용 상황
| 상황 | 이유 | 
| 복잡한 조인 / 서브쿼리 | JPQL로 표현하기 어려운 경우 | 
| DB 특화 기능 사용 | 예: 윈도우 함수, 특정 DB 함수 등 | 
| 성능 최적화 | JPQL보다 효율적인 쿼리 필요 시 | 
| 기존 SQL 자산 재사용 | DB에 이미 작성된 쿼리를 그대로 쓰고 싶을 때 | 
예제) Native SQL
더보기
@Test
@Transactional
void native_query_entity() {
    String sql = "SELECT ID, NAME, NICKNAME, AGE, TEAM_ID FROM USER WHERE AGE > ?";
    Query nativeQuery = entityManager.createNativeQuery(sql, User.class).setParameter(1, 20);
    List<User> result = nativeQuery.getResultList();
    for (User row : result) {
        System.out.println(row);
    }
}- SELECT문에 누락된 컬럼이 있으면 예외 발생 ‼
- JPA는 Native SQL 사용 시, 엔티티 매핑을 위해 컬럼 명과 엔티티 필드를 1:1 매핑하려고 합니다.
@Test
@Transactional
void native_query_object() {
    String sql = "SELECT ID, NAME, NICKNAME, AGE, TEAM_ID FROM USER WHERE AGE > ?";
    Query nativeQuery = entityManager.createNativeQuery(sql).setParameter(1, 20);
    List<Object[]> result = nativeQuery.getResultList();
    for (Object[] row : result) {
        System.out.println(row[0]);
        System.out.println(row[1]);
        System.out.println(row[2]);
        System.out.println(row[3]);
    }
}- Object로 결과 받을수도 있음
2. Named Native Query
- Native Query를 사전에 정의합니다.
- 엔티티 클래스에 @NamedNativeQuery로 정의
- 간단한 쿼리일 경우, 직접 정의 가능
 
- 컴파일 시점에 정해지며, 재사용성이 높음
예제
더보기
@NamedNativeQuery(
    name = "User.findAll",
    query = "SELECT * FROM springboot_document.user",
    resultClass = User.class
)
@Entity
@Getter
@Setter
@NoArgsConstructor
@Access(AccessType.FIELD)
public class User {@Test
@Transactional
void native_query_result_class() {
    Query nativeQuery = entityManager.createNamedQuery("User.findAll");
    List<User> result = nativeQuery.getResultList();
    for (User row : result) {
        System.out.println(row);
    }
}
결과 매핑 어노테이션
| 어노테이션 | 적용 대상 | 설명 | 
| @SqlResultSetMapping | 클래스 레벨 | 결과 매핑 정의를 모아놓는 컨테이너 역할 | 
| @EntityResult | @SqlResultSetMapping 내부 | 엔티티 매핑을 정의 (엔티티 클래스 지정) | 
| @FieldResult | @EntityResult 내부 | 결과 컬럼과 엔티티 필드 간 매핑 지정 | 
| @ColumnResult | @SqlResultSetMapping 내부 | 스칼라(단일 값) 타입 매핑 | 
| @ConstructorResult | @SqlResultSetMapping 내부 | DTO 생성자 기반 매핑 | 
| @NamedNativeQuery | 클래스 레벨 | SQL과 결과 매핑을 함께 명시 가능 | 
예제) 결과 매핑 어노테이션
더보기
@SqlResultSetMapping(
    name = "UserEntityAndDtoMapping",
    entities = {
        @EntityResult(
            entityClass = User.class,
            fields = {
                @FieldResult(name = "id", column = "id"),
                @FieldResult(name = "name", column = "name"),
                @FieldResult(name = "nickname", column = "nickname"),
                @FieldResult(name = "age", column = "age"),
                @FieldResult(name = "team", column = "team_id")
            }
        )
    },
    columns = {
        @ColumnResult(name = "id", type = Integer.class),
        @ColumnResult(name = "name", type = String.class)
    },
    classes = {
        @ConstructorResult(
            targetClass = UserResponse.class,
            columns = {
                @ColumnResult(name = "id", type = Integer.class),
                @ColumnResult(name = "name", type = String.class)
            }
        )
    }
)
@NamedNativeQuery(
    name = "User.findUserAndDto",
    query = "SELECT id, name, nickname, age, team_id FROM springboot_document.user WHERE age > :minAge",
    resultSetMapping = "UserEntityAndDtoMapping"
)
@Entity
@Getter
@Setter
@NoArgsConstructor
@Access(AccessType.FIELD)
public class User {@Test
@Transactional
void native_query_result_mapping() {
    Query nativeQuery = entityManager.createNamedQuery("User.findUserAndDto")
            .setParameter("minAge", 20);
    List<Object[]> result = nativeQuery.getResultList();
    for (Object[] row : result) {
        User user = (User) row[0];
        UserResponse dto = (UserResponse) row[1];
        System.out.println(user);
        System.out.println(dto);
        System.out.println();
    }
}
3. 스토어드 프로시저
- @NamedStoredProcedureQuery 로 이름 지정 가능
- 복잡한 로직을 DB에 위임 가능
예제
더보기
CREATE PROCEDURE proc_multiply (INOUT inParam INT, INOUT outParam INT)
BEGIN
    SET outParam = inParam * 2;
END;@NamedStoredProcedureQuery(
    name = "multiply",
    procedureName = "proc_multiply",
    parameters = {
        @StoredProcedureParameter(name = "inParam", mode = ParameterMode.IN, type = Integer.class),
        @StoredProcedureParameter(name = "outParam", mode = ParameterMode.OUT, type = Integer.class),
    }
)
@Entity
@Getter
@Setter
@NoArgsConstructor
@Access(AccessType.FIELD)
public class User {@Test
@Transactional
void native_query_stored_procedure_declaratively() {
    StoredProcedureQuery spq = entityManager.createNamedStoredProcedureQuery("multiply");
    spq.setParameter("inParam", 100);
    spq.execute();
    assertEquals((Integer) spq.getOutputParameterValue("outParam"), 200);
}@Test
@Transactional
void native_query_stored_procedure_programmatibly() {
    StoredProcedureQuery spq = entityManager.createStoredProcedureQuery("proc_multiply");
    spq.registerStoredProcedureParameter(1, Integer.class, ParameterMode.IN);
    spq.registerStoredProcedureParameter(2, Integer.class, ParameterMode.OUT);
    spq.setParameter(1, 100);
    spq.execute();
    assertEquals((Integer) spq.getOutputParameterValue(2), 200);
}
'Spring > Spring Data JPA' 카테고리의 다른 글
| [자바 ORM 표준 JPA 프로그래밍] 12. 스프링 데이터 JPA (0) | 2025.04.25 | 
|---|---|
| [자바 ORM 표준 JPA 프로그래밍] 10-5. 객체지향 쿼리 언어: 객체지향 쿼리 심화 (0) | 2025.04.24 | 
| [자바 ORM 표준 JPA 프로그래밍] 10-2. 객체지향 쿼리 언어: Criteria (0) | 2025.04.24 | 
| [자바 ORM 표준 JPA 프로그래밍] 10-1. 객체지향 쿼리 언어: JPQL (0) | 2025.04.24 | 
| [Spring Data JPA] 3-4. Specifications (0) | 2024.08.03 |