Native Query
Native Query란?
Native Query를 의역하면 '순수 쿼리'가 된다.
말그대로 흔히 사용한 쿼리문을 순수하게 가져온 것이다.
[사용방법]
@Query(value = "select * from book", nativeQuery = true)
List<Book> findAllCustom();
사용법은 위 코드처럼 쿼리문을 value로 입력한 뒤, nativeQuery를 true로 선언하면 된다.
보다시피 쿼리문은 JPQL이 아닌 일반 쿼리문을 쌩으로 작성한 것과 동일하다.
[JPQL 예시]
@Query(value = "select new com.fastcampus.jpa.bookmanager.repository.dto.BookNameAndCategory(b.name, b.category) from Book b")
List<BookNameAndCategory> findBookNameAndCategory();
native 쿼리의 특징으로는 entity 속성을 사용 못한다는 점이 있다.
따라서 dialect를 활용하지 않아 특정 DB에 의존성을 가진 Query를 만들게 된다.
이로인해 JPA의 장점과는 약간 거리가 있다.
Native Query 실제 사용 예시
하나는 JPA Repository에 존재하는 finder를 사용해보고, 다른 하나는 위에 예시로 작성한 Native Query를 사용해보자
@Test
void nativeQueryTest(){
bookRepository.findAll().forEach(System.out::println); // JPA Repository에 존재하는 finder
bookRepository.findAllCustom().forEach(System.out::println); // Native Query
}
[JPA Repository의 FindAll]
Hibernate:
select
book0_.id as id1_2_,
book0_.created_at as created_2_2_,
book0_.updated_at as updated_3_2_,
book0_.author_id as author_i4_2_,
book0_.category as category5_2_,
book0_.deleted as deleted6_2_,
book0_.name as name7_2_,
book0_.publisher_id as publishe8_2_
from
book book0_
where
(
book0_.deleted = 0
)
where 조건을 보면 deleted가 false인 값만 출력한다.
deleted가 false인 값만 출력하는 이유는 Book 클래스에 다음과 같이 논리삭제를 구현했기 때문이다.
@Where(clause = "deleted = false")
public class Book extends BaseEntity{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
...
Book(super=BaseEntity(createdAt=2021-09-29T01:53:45.382900, updatedAt=2021-09-29T01:53:45.382900), id=1, name=JPA 초격차 패키지, category=null, authorId=null, deleted=false)
Book(super=BaseEntity(createdAt=2021-09-29T01:53:45.384995, updatedAt=2021-09-29T01:53:45.384995), id=2, name=Spring Security 초격차 패키지, category=null, authorId=null, deleted=false)
결과적으로 Book에 저장된 3개의 데이터 중 deleted가 false인 2개가 출력됐다.
[Native Query]
Book(super=BaseEntity(createdAt=2021-09-29T01:53:45.382900, updatedAt=2021-09-29T01:53:45.382900), id=1, name=JPA 초격차 패키지, category=null, authorId=null, deleted=false)
Book(super=BaseEntity(createdAt=2021-09-29T01:53:45.384995, updatedAt=2021-09-29T01:53:45.384995), id=2, name=Spring Security 초격차 패키지, category=null, authorId=null, deleted=false)
Book(super=BaseEntity(createdAt=2021-09-29T01:53:45.386093, updatedAt=2021-09-29T01:53:45.386093), id=3, name=Spring 올인원 패키지, category=null, authorId=null, deleted=true)
Native Query에서는 deleted 조건과 관계없이 모든 데이터를 출력했다.
말 그대로 별다른 Entity의 속성을 사용하지 않고 select * from book를 했기 때문이다.
Native Query 잘 안써요
일반적으로 운영 DB는 MySQl, Oracle등 다양한 DB를 사용하고 테스트 코드의 경우 H2 DB로 처리한다.
H2 DB의 경우 일반적인 DB와 비슷한 기능을 제공하지만, 간혹 몇몇 기능에서 차이점이 발생한다.
때문에 Native Query의 경우 일부 코드에서 오작동이 발생할 수 있으므로, Native Query는 최소한으로 사용하는 것이 좋다.
그럼 대체 언제써요?
결론적으로 DB 마이그레이션의 성능에 대한 문제를 해결하기 위해서 쓰인다.
예를들어 데이터 마이그레이션에서 어떤 DB의 데이터를 모두 날려야하는 경우, delete query를 사용하면 한 번에 모든 데이터를 삭제할 수 있다. 반면 update의 경우 그렇지 않다. 하나하나 조회를 해서 save를 해아한다. 이는 for문을 돌려서 하나하나 데이터를 다른 값으로 저장하는 과정과 동일할 것이다.
수십만, 수백만 건이 넘는 데이터를 마이그레이션하면서 동시에 데이터를 변형하는 작업까지 한다면, 그 많은 데이터를 일일이 조회하고 또 일일이 수정하는 작업이 진행될 것이다. Native Query를 쓰면 위와같은 상황을 개선할 수 있다.
@Transactional
@Modifying // 업데이트 표시
@Query(value = "update book set category = 'IT전문서'", nativeQuery = true)
int updateCategories();
위의 코드를 보자. Native Query를 작성하여 book에 있는 모든 DB의 category가 'IT전문서'로 저장된다.
int로 함수를 선언했으니 업데이트 된 Row의 줄을 출력할 것이다.
이렇게 Native Query를 작성하면 마치 delete query를 사용하여 한 번에 모든 데이터를 삭제하는 것 처럼, 데이터의 업데이트가 한 번에 이루어진다.