# Native Query

* SQL을 직접 짜는 방식
* 정말 어쩔 수 없을 때만 사용한다.
  * 최근에는 스프링 데이터 Projection을 활용한다.

## 스프링 데이터 JPA 기반 네이티브 쿼리

* 페이징 지원
* 반환 타입
  * Object\[]
  * Tuple
  * DTO
    * 스프링 데이터 인터페이스인 Projections 지원

{% tabs %}
{% tab title="MemberRepository.java" %}

```java
public interface MemberRepository extends JpaRepository<Member, Long> {

    @Query(value = "select * from member where username = ?", nativeQuery = true)
    Member findByNativeQuery(String username);

}
```

{% endtab %}

{% tab title="MemberRepositoryTest.java" %}

```java
class MemberRepositoryTest {

    @Test
    void nativeQuery() {
        Team teamA = new Team("teamA");
        em.persist(teamA);

        Member m1 = new Member("m1", 0, teamA);
        Member m2 = new Member("m2", 0, teamA);

        em.persist(m1);
        em.persist(m2);

        em.flush();
        em.clear();

        Member result = memberRepository.findByNativeQuery("m1");
    }
}
```

{% endtab %}
{% endtabs %}

```sql
select *
from member
where username = 'm1';
```

* 조회하려는 필드를 select 절에 다 적어줘야 해서 불편하다.
* JPQL처럼 애플리케이션 로딩 시점에 문법을 확인하는 것이 불가하다.
* 동적 쿼리를 쓸 수 없다.
* 반환 타입이 제한되어 있다.
* Sort 파라미터를 통한 정렬이 정상 동작 하지 않을 수도 있다.
  * 믿지 말고 직접 처리하자.
* JPQL은 위치 기반 파라미터를 1부터 시작하지만 SQL은 0부터 시작한다.

## Projections 활용

* 스프링 데이터 JPA 네이티브 쿼리와 인터페이스 기반 Projections를 활용할 수 있다.

{% tabs %}
{% tab title="MemberProjection.java" %}

```java
public interface MemberProjection {

    Long getId();

    String getUsername();

    String getTeamUsername();

}
```

{% endtab %}

{% tab title="MemberRepository.java" %}

```java
public interface MemberRepository extends JpaRepository<Member, Long> {

    @Query(value = "SELECT m.member_id as id, m.username, t.name as teamName " +
            "FROM member m left join team t",
            countQuery = "SELECT count(*) from member",
            nativeQuery = true)
    Page<MemberProjection> findByNativeProjection(Pageable pageable);

}
```

{% endtab %}

{% tab title="MemberRepositoryTest.java" %}

```java
class MemberRepositoryTest {

    @Test
    void nativeQuery() {
        ...

        // 인덱스가 0부터 시작하므로 0으로 조회한다.
        Page<MemberProjection> result = memberRepository.findByNativeProjection(PageRequest.of(0, 10));
    }
}
```

{% endtab %}
{% endtabs %}

```sql
SELECT m.member_id as id, m.username, t.name as teamName
FROM member m
         left join team t limit 10;

SELECT count(*)
from member;
```

## 동적 네이티브 쿼리

```java
class Example {
    public static void main(String[] args) {
        String sql = "select m.username as username from member m";

        List<MemberDto> result = em.createNativeQuery(sql)
                .setFirstResult(0)
                .setMaxResults(10)
                .unwrap(NativeQuery.class)
                .addScalar("username")
                .setResultTransformer(Transformers.aliasToBean(MemberDto.class))
                .getResultList();
    }
}
```

* 하이버네이트를 직접 활용한다.
* 스프링 JdbcTemplate, myBatis, jooq 같은 외부 라이브러리를 사용하는걸 추천한다.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://dodeon.gitbook.io/study/kimyounghan-spring-data-jpa/06-etc/04-native-query.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
