일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- spring
- JDBC
- Exception
- Proxy
- SpringBoot
- 스프링 핵심 기능
- 김영한
- 자바
- jpa
- Spring Boot
- 스프링 핵심 원리
- 알고리즘
- kotlin
- springdatajpa
- Servlet
- Thymeleaf
- db
- java
- pointcut
- QueryDSL
- AOP
- Android
- transaction
- 인프런
- JPQL
- 스프링
- 백준
- 그리디
- Greedy
- http
- Today
- Total
개발자되기 프로젝트
Native Query 본문
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
'JPA > Custom Query' 카테고리의 다른 글
@Converter (0) | 2021.07.10 |
---|---|
@Query (0) | 2021.07.09 |