[JPA] Native Query

jpa, spring

Native Query #

Native Query는 앞에서 배웠던 @Query 에서 아래와 같이 nativeQuery = true 값만 지정해주면 된다.

@Query(value = "select * from book", nativeQuery = true)
List<Book> findAllCustom();

아래의 테스트를 통하여 native query는 무엇이 다른지 알아보자.

@Test
void nativeQueryTest() {
    bookRepository.findAll().forEach(System.out::println);
    bookRepository.findAllCustom().forEach(System.out::println);
}

아래는 첫번째 줄의 테스트값이다. 일전에 지정해놓은 where 값도 들어가서 deleted flag가 false인 결과값만 도출해낸다.

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
        )

Book(super=BaseEntity(createdAt=2021-12-26T16:43:07.902964, updatedAt=2021-12-26T16:43:07.902964), id=1, name=JPA 초격차 패키지, category=null, authorId=null, deleted=false)
Book(super=BaseEntity(createdAt=2021-12-26T16:43:07.917569, updatedAt=2021-12-26T16:43:07.917569), id=2, name=Spring Security 초격차 패키지, category=null, authorId=null, deleted=false)

native query를 사용한 두번째 결과값은 아래와 같다. @Query value에 적어놓은 값만 충실히 사용함으로써 3개의 db가 도출된다.

Hibernate: 
    select
        * 
    from
        book

Book(super=BaseEntity(createdAt=2021-12-26T16:43:07.902964, updatedAt=2021-12-26T16:43:07.902964), id=1, name=JPA 초격차 패키지, category=null, authorId=null, deleted=false)
Book(super=BaseEntity(createdAt=2021-12-26T16:43:07.917569, updatedAt=2021-12-26T16:43:07.917569), id=2, name=Spring Security 초격차 패키지, category=null, authorId=null, deleted=false)
Book(super=BaseEntity(createdAt=2021-12-26T16:43:07.928287, updatedAt=2021-12-26T16:43:07.928287), id=3, name=SpringBoot AllInOne 패키지, category=null, authorId=null, deleted=true)

Native Query 의 이점 1 #

update query에는 deleteAll 처럼 한번에 실행하는 메소드가 없기때문에

@Test
void nativeQueryTest() {
    List<Book> books = bookRepository.findAll();
    
    for(Book book : books) {
        book.setCategory("IT 전문서");
    }
    bookRepository.saveAll(books);
    System.out.println(bookRepository.findAll());
}

그러므로 아래와 같이 마이그래이션으로 배치하여 사용하는 경우가 많다.

@Transactional
@Modifying
@Query(value = "update book set category = 'IT전문서'", nativeQuery = true)
int updateCategories();

그리고 아래와 같이 testing을 진행하면,

@Test
void nativeQueryTest() {
    System.out.println("affected rows : " + bookRepository.updateCategories());
    bookRepository.findAllCustom().forEach(System.out::println);
}

아래와 같이 update를 한번만 때린다.

Hibernate: 
    update
        book 
    set
        category = 'IT전문서'
affected rows : 3

Book(super=BaseEntity(createdAt=2021-12-26T17:19:05.210579, updatedAt=2021-12-26T17:19:05.210579), id=1, name=JPA 초격차 패키지, category=IT전문서, authorId=null, deleted=false)
Book(super=BaseEntity(createdAt=2021-12-26T17:19:05.232064, updatedAt=2021-12-26T17:19:05.232064), id=2, name=Spring Security 초격차 패키지, category=IT전문서, authorId=null, deleted=false)
Book(super=BaseEntity(createdAt=2021-12-26T17:19:05.240665, updatedAt=2021-12-26T17:19:05.240665), id=3, name=SpringBoot AllInOne 패키지, category=IT전문서, authorId=null, deleted=true)

하지만 여기에서는 위와같이 deleted 된 DB까지 함께 update 되는것을 알 수 있다.

Native Query 의 이점 2 #

두번째 native query를 사용하는 경우는 JPA에서 기본적으로 지원하지않는 기능을 사용할때 사용한다.

@Query(value = "show tables", nativeQuery = true)
List<String> showTables();

위와 같이 show tables라는 쿼리 기능을 사용할 수 있다.