Notice
Recent Posts
Recent Comments
Link
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

개발자되기 프로젝트

Native Query 본문

JPA/Custom Query

Native Query

Seung__ 2021. 7. 10. 15:14
 

@Query

@Query를 사용하는 이유!에 대해 알아보자  1. 이름이 길어지는 경우 가독성을 좋게 변경하기 위해 다음과 같이 이름이 굉장히 킨 query method가 있다고 해보자. List findByCategoryIsNull AndNameEquals AndCr..

bsh-developer.tistory.com

 

1. Native Query?


JPA에서 JPQL을 사용할 수 없을 때, SQL을 직접 사용할 수 있는 옵션이다.

 

dialect를 사용할 수 없다, 따라서 특정 DB에 의존성을 가진 query를 만들게됨.

 

즉, DB종류가 변경되면 code변경시 자동으로 해당 DB를 반영해주는 JPA기능 활용이 불가능.

 

 

 

2. 사용법


기존 @Query에서 nativeQuery = true를 적용하면 된다.

 

[native Query]

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

[JPQL]

@Query(value = "select b from Book b")

native query와 JPQL의 차이점은 nativeQuery는 Entity속성은 사용하지 못한다.

 

따라서 Book은 table이름인 book으로 입력되어야 한다.

 

 

3 JPQL vs. NativeQuery


JPQL query와 nativeQuery를 비교해보자.

@Test
void nativeQueryTest(){

  System.out.println("JPQL : findAll");
  bookRepository.findAll().forEach(System.out::println);

  System.out.println("SQL : findAllCustom");
  bookRepository.findAllCustom().forEach(System.out::println);
  
}

 

JPQL은 이전 글에서 입력한 @Where(clause = "deleted = false") 에 의해 해당 구문이 적용되어 query가 실행된다.

즉 findAll을 실행하면 findAllDeletedFalse로 실행되는 것과 같다.

 

하지만 nativeQuery는 @Where가 적용되지 않아, deleted =true인 date까지 조회가 되었다. 

 

JPQL : findAll

Hibernate: 
    select
        book0_.id as id1_1_,
        book0_.created_at as created_2_1_,
        book0_.updated_at as updated_3_1_,
        book0_.author_id as author_i4_1_,
        book0_.category as category5_1_,
        book0_.deleted as deleted6_1_,
        book0_.name as name7_1_,
        book0_.publisher_id as publishe8_1_ 
    from
        book book0_ 
    where
        (
            book0_.deleted = 0
        )

Book(super=BaseEntity(createdAt=2021-07-10T12:40:37.754561, updatedAt=2021-07-10T12:40:37.754561), id=1, name=탐사수수수수, authorId=null, category=null, deleted=false)
Book(super=BaseEntity(createdAt=2021-07-10T12:40:37.762733, updatedAt=2021-07-10T12:40:37.762733), id=2, name=삼다수, authorId=null, category=null, deleted=false)

SQL : findAllCustom

Hibernate: 
    select
        * 
    from
        book

Book(super=BaseEntity(createdAt=2021-07-10T12:40:37.754561, updatedAt=2021-07-10T12:40:37.754561), id=1, name=탐사수수수수, authorId=null, category=null, deleted=false)
Book(super=BaseEntity(createdAt=2021-07-10T12:40:37.762733, updatedAt=2021-07-10T12:40:37.762733), id=2, name=삼다수, authorId=null, category=null, deleted=false)
Book(super=BaseEntity(createdAt=2021-07-10T12:40:37.766614, updatedAt=2021-07-10T12:40:37.766614), id=3, name=백산수, authorId=null, category=null, deleted=true)

 

JPQL vs NativeQuery

  JPQL NativeQuery
@Where 포함 미포함
dialect 활용 미활용
DB의존성 낮음 높음

 

 

 

3. 왜쓿까? :성능문제 해결

 

JpaRepository에서 제공하는 기능 중,  deleteAll(), deleteAllInBatch()를 보자.

deleteAll() findAll -> deleteById -> .....
deleteAllInBatch() 바로 삭제.

delete Query의 경우 모든 record를 조회하지 않고 삭제할 수 있는 쿼리를 제공함.

 

update query를 각 record를 조회해서 하나하나 save를 함.

 

즉 이처럼 record 하나하나 처리를 하는 상황이 성능문제를 일으킬 수 있다.

 

 

 

 

1) test : 각 record별로 update하도록 해보자.

 

repository에서 book을 모두 가져와서 각 entity마다 category를 변경하고 saveAll

   @Test
    void nativeQueryTest(){

      List<Book> books = bookRepository.findAll();
      
      for(Book book : books){

      book.setCategory("백엔드");

      }
      
      bookRepository.saveAll(books);

      System.out.println(bookRepository.findAll());


    }

 

update query가 각각 진행되었다.

Hibernate: 
    update
        book 
    set
        updated_at=?,
        author_id=?,
        category=?,
        deleted=?,
        name=?,
        publisher_id=? 
    where
        id=?
Hibernate: 
    update
        book 
    set
        updated_at=?,
        author_id=?,
        category=?,
        deleted=?,
        name=?,
        publisher_id=? 
    where
        id=?

id값을 가져와서 update를 여러번 실행하는 것은 data수가 많아진다면 성능문제를 일으킬 수 있다. 느려

 

그렇다면 deleteAllInBatch()처럼 한 번의 query로 싹 다 update할 수 없을 까?

 

 

4. 한 번의  query로 여러record update


1) return 타입 설정

 

<BookRepository>

@Query(value = "update book set category = '백엔드'", nativeQuery = true)
int updateCategories();

int로 반환을 받는 이유는 affected row, 즉 update된 row수 return받기위해.(long일수도..)

 

일반적으로 dml update, delte작업에서는 return되는 값이 단순히 작업된 row의 수다.

 

따라서 nativeQuery를 사용할 경우 해당 query 가 update qury시  @modifying를 통해 update라고 반영되어야함.

 

 

2) @Transactional 설정

 

또한 save method는 자체적으로 @Trnasactional이 적용되어, 상위에서 Transaction이 없으면 각 save가 transaction을 생성했다.

 

하지만 native query 사용시 해당 기능을 사용할 수 없다. 따라서 @Transactional을 직접 정의해야함.

@Transactional
@Modifying
@Query(value = "update book set category = '백엔드'", nativeQuery = true)
int updateCategories();

<Test>

@Test
void nativeQueryTest(){

    List<Book> books = bookRepository.findAll();
    for(Book book : books){

    book.setCategory("백엔드드");

    }
    
    bookRepository.saveAll(books);

    System.out.println(bookRepository.findAll());

    System.out.println("affected rows : " + bookRepository.updateCategories());
    System.out.println(bookRepository.findAllCustom());

}

테스트 결과 native query를 사용하여 여러 record에 대한 update를 한 번의 query로 완료했다.

Hibernate: 
    update
        book 
    set
        category = '백엔드'
affected rows : 3

 

* 참고) @Transactional을 interface에 적용한 경우

 

 

 

그리고! 마지막줄에서 native query 를 통해 findAll을 실행했다.

 

따라서 @Where가 적용되지 않아, deleted = true인 data도 조회가 되었다.

 

문제는 삭제된 data까지 category가 변경되었다.

 

위에서 말한 것 처럼 native query를 사용한 경우 입력한 문자열 그대로 실행이된다.

Book(Super=BaseEntity(createdAt=2021-07-10T13:23:03.484591, 
  updatedAt=2021-07-10T13:23:07.203988), id=1, name=탐사수수수수, authorId=null, category=백엔드, 
  deleted=false), 
Book(super=BaseEntity(createdAt=2021-07-10T13:23:03.489198, 
  updatedAt=2021-07-10T13:23:07.208642), id=2, name=삼다수, authorId=null, category=백엔드, 
  deleted=false), 
Book(super=BaseEntity(createdAt=2021-07-10T13:23:03.490090, 
  updatedAt=2021-07-10T13:23:03.490090), id=3, name=백산수, authorId=null, category=백엔드, 
  deleted=true)]

 

 

 

 

5. JPA에서 지원하지 않는 기능을 사용


와 이런걸 자바로 짜야하나????? 파이썬이나 다른걸로 하면 안되나?????

 

예를들어 DB의 tables를 보기위해 사용하는 show tables 의 경우 일반적인 JPA query로 불가능...

 

킹치만 native query를 사용하면 쌉가능

 

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

왜나하면 value에 있는 문자열 그대로 DB에서 실행되기 때문.

Hibernate: 
    show tables
[author, book, book_and_author, book_review_info, publisher, review, user, user_history]

 

 

6.GitHub : 210710 native Query


 

 

bsh6463/BookManager

Contribute to bsh6463/BookManager development by creating an account on GitHub.

github.com

 

'JPA > Custom Query' 카테고리의 다른 글

@Converter  (0) 2021.07.10
@Query  (0) 2021.07.09
Comments