NativeQuery 속성만 true로해주면 생성이 된다.
@Query(value = "select * from Book", nativeQuery = true)
List<Book> findAllCustom();
여기서 중요한 부분은 JPQL과 다르게 엔티티 속성을 사용하지 못한다. 쿼리의 from에는 테이블명이 들어가야한다. 속성들을 뿌려줘야 한다면 createdAt같은 경우는 created_at과 같이 컬럼이름을 작성해줘야한다.
@SQLRestriction같은 경우 nativeQuery에서는 포함되어 있지않다.
말 그대로 db에서 사용하는 sql쿼리를 그대로 사용하는 것이기 때문에 쿼리문의 text(value)가 db에서 그대로 실행된다.
또한, dialect를 활용하지 않기 때문에 특정 db에 의존성을 가지는 쿼리를 만들게 된다. 즉, db종류가 변경이 되었을 때 코드변경없이 쿼리를 자동으로 바꿔주는 jpa장점에서 빗겨나는 기능이다.
하지만 자주 사용하는 이종 db가 존재한다. 바로 h2 embeded db이다. 운영 db는 mysql, oracle등..을 사용하고 테스트 코드는 h2 db를 사용해 처리하는것이 일반적이다.
테스트와 운영환경에서 이종 db를 함께 사용하는데 h2 db에서는 특정 db에 대한 호환모드를 제공하지만 몇 가지 부분에서 차이가 생겨 낮은 확률로 특수한 케이스에서 오작동을 할 수 있다. 그렇기 때문에 native query를 사용해야하는 경우가 생긴다. 그렇기에 native쿼리는 최소한으로 사용하는 것이 좋다.
JpaRepository에서 제공하는 메서드 중에서 deleteAllinBatch라는 것이 있다. deleteAll로 실행하는 경우에는 findAll을 실행한뒤 하나하나 아이디 값으로 delete를 실행한다. 하지만 deleteAllinBatch의 경우에는 조건없는 delete from book 하나로 모든것을 삭제한다.
delete쿼리의 경우에는 모든레코드를 조회하지 않아도 한번에 처리할 수있는 메소드를 제공하고 있지만 update쿼리는 어떨까?
하나 하나 조회해서 save하도록 제공하고 있다.
@Test
void nativeQueryTest() {
List<Book> books = bookRepository.findAll();
for(Book book : books) {
book.setCategory("IT전문서");
}
bookRepository.saveAll(books);
System.out.println(bookRepository.findAll());
}
Hibernate:
update
book
set
author_id=?,
category=?,
deleted=?,
name=?,
publisher_id=?,
updated_at=?
where
id=?
Hibernate:
update
book
set
author_id=?,
category=?,
deleted=?,
name=?,
publisher_id=?,
updated_at=?
where
id=?
[Book(super=BaseEntity(createdAt=2024-01-31T17:03:31.268256, updatedAt=2024-01-31T17:03:32.052649),
id=1, name=JPA 초격차 패키지, category=IT전문서, authorId=null, deleted=false),
Book(super=BaseEntity(createdAt=2024-01-31T17:03:31.270735, updatedAt=2024-01-31T17:03:32.067629),
id=2, name=Srping Security 초격차 패키지, category=IT전문서, authorId=null, deleted=false)]
테스트를 해보면 id값으로 하나하나 update를 여러번 실행하고 있다. 만일 대량의 데이터에 대한 마이그레이션으 실행한다면 속도의 저하를 가져오게된다. 그래서 deleteAllinBatch처럼 한번의 쿼리로 실행 할수 있게끔 native query를 사용한다.
@Query(value = "update book set category = 'IT전문서'", nativeQuery = true)
int updateCategories();
이렇게 하면 한번에 update가 된다. 그렇지만 테스트 실행시
org.springframework.orm.jpa.JpaSystemException: JDBC exception executing SQL [update book set category = 'IT전문서'] [Statement.executeQuery() cannot issue statements that do not produce result sets.] [n/a] 에러가 뜬다.
일반적으로 DML작업에서는 리턴 되는 값이 적용된 row갯수를 표현해준다. @Modifying로 update라는 것을 표시해주면 int값에 해당값이 리턴된다. 또한 native query를 실행하는 경우 @Transactional을직접 선언해줘야한다.
@Transactional
@Modifying
@Query(value = "update book set category = 'IT전문서'", nativeQuery = true)
int updateCategories();
Hibernate:
update
book
set
category = 'IT전문서'
affected rows : 3
update쿼리하나로 실행된것을 볼 수 있다.
하지만 spring 공식문서에서는 interface보다는 구체 클래스에 사용하는 것을 권장하므로 상황에 맞게 어노테이션을 다는게 좋다.
SQL터미널에서는 show tables;같은 명령어로 사용하고 있는 db의 테이블들을 조회 할 수 있다. 하지만 jpa에서는 기본적으로 지원해주지 않기에 native query로 작성하여 사용한다.
@Query(value = "show tables", nativeQuery = true)
List<String> showTables();
Hibernate:
show tables
[address, author, book, book_and_author, book_review_info, member, member_history, publisher, review]