[Spring] Native Query 사용

WOOK JONG KIM·2022년 11월 23일
0

패캠_java&Spring

목록 보기
64/103
post-thumbnail

Native Query

@Query 어노테이션에서 nativeQuery 속성을 true로 설정하면 사용 가능

JPQL쿼리와 다르게 Entity 속성을 사용할 수 없음

JPA에 Dialect(방언)을 사용할 수 없음

테스트(h2)와 운영(MySQL)에서 이종 데이터베이스를 함께 사용할 경우 특수형 경우 Native Query 오작동 가능

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

이때는 컬럼 네임을 그대로 기록해야 함

@Test
void nativeQueryTest() {
    bookRepository.findAll().forEach(System.out::println); // JPA
    bookRepository.findAllCustom().forEach(System.out::println); //native Query
}
//JPA Repositry 사용 
//JPQL 사용시 쿼리에 alias가 자동생성
//Entity 설정에 Where(deleted : false만 출력) 설정으로 인해 조건이 생성
Hibernate: 
    select
        book0_.id as id1_2_,
        book0_.created_at as created_2_2_,
        book0_.updated_at as updated_3_2_,
        book0_.category as category4_2_,
        book0_.deleted as deleted5_2_,
        book0_.name as name6_2_,
        book0_.publisher_id as publishe7_2_ 
    from
        book book0_ 
    where
        (
            book0_.deleted = 0
        )

Book(super=BaseEntity(createdAt=2022-11-23T15:36:13, updatedAt=2022-11-23T15:36:13), id=1, name=JPA 초격차 패키지, category=null, authorId=null, deleted=false)
Book(super=BaseEntity(createdAt=2022-11-23T15:36:13, updatedAt=2022-11-23T15:36:13), id=2, name=스프링 시큐리티 패키지, category=null, authorId=null, deleted=false)

//native Query 사용
//작성한 native Query만 실행
Hibernate: 
    select
        * 
    from
        book
        
// dialect를 사용 안하기에 특정 DB에 의존성을 가지는 쿼리 생성

Book(super=BaseEntity(createdAt=2022-11-23T15:36:13, updatedAt=2022-11-23T15:36:13), id=1, name=JPA 초격차 패키지, category=null, authorId=null, deleted=false)
Book(super=BaseEntity(createdAt=2022-11-23T15:36:13, updatedAt=2022-11-23T15:36:13), id=2, name=스프링 시큐리티 패키지, category=null, authorId=null, deleted=false)
Book(super=BaseEntity(createdAt=2022-11-23T15:36:13, updatedAt=2022-11-23T15:36:13), id=3, name=스프링 부트 패키지, category=null, authorId=null, deleted=true)

Native 쿼리를 사용하는 이유

  1. 성능에 대한 문제를 해결하는데 이용 (대표적으로 update)
  • delete는 한번에 삭제하는 메소드를 제공 하는 반면
    ->
    deleteAllInBatch : 조건 없는 삭제 (delete from book;)
    deleteInBatch : findAll 한 후 레코드 하나하나를 id값으로 delete

  • update는 한개씩 처리하는 방법만 제공 (saveAll도 findAll 후 id값으로 수정)

BookRepository

@Repository
public interface BookRepository extends JpaRepository<Book, Long> {
		...

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

여기서 DML(update, insert, delete) 사용시 에는 @Modifying을 명시해 주어야 함 -> 이들은 affected rows 수, 즉 int를 반환

-> 이후 TransactionRequiredException 발생

save 같은 메서드를 보면 @Tranactional이 붙어 있는데
-> 이때 메서드는 호출하는 쪽에서 트랜잭션을 가지고 있다면 어노테이션이 무시되지만, 트랜잭션이 없다면 자체적으로 트랜잭션을 가지고 메서드 내용을 처리

@Test
void nativeQueryTest() {
    System.out.println("affected rows : " + bookRepository.updateCategories());
    bookRepository.findAllCustom().forEach(System.out::println);
}
//bookRepository.updateCategories()
Hibernate: 
    update
        book 
    set
        category = 'IT전문서'

affected rows : 3

//bookRepository.findAllCustom()
Hibernate: 
    select
        * 
    from
        book
        
// native query를 사용하였기에 deleted:true 컬럼 까지 업데이트 되어 로우 수 3이 출력됨        

결과는 제대로 나왔지만 우리가 @Transactional을 interface인 BookRepository에 선언하엿음

@Transactional은 interface보다 concrete 클래스에서 사용하기를 권고
-> interface는 interface-base-proxies 경우에 사용 (JpaRepository는 proxies라 문제 X)

만약 Repository에 @Transactional 사용하지 않은 경우 각 클레스마다 설정해야하는 불편함과 문제 발생 -> ex) nativeQueryTest() 위에 @Transactional 선언

상황에 맞게 @Tranactional 선언 하자

  1. JPA에서 제공하지 않는 기능을 사용하는 경우

show tables;, show databases; 과 같은 기능은 JPA에서 제공하지 않음

@Test
void nativeQueryTest() {
    System.out.println(bookRepository.showTables());
}

//실행결과
Hibernate: 
    show tables

[address, author, book, book_and_author, book_review_info, publisher, review, user, user_history]
profile
Journey for Backend Developer

0개의 댓글