[JPA] 객체 지향 쿼리 언어 - Native SQL
네이티브 SQL
JPQL은 표준 SQL이 지원하는 대부분의 문법과 SQL함수를 지원한다.
근데 특정 DB의 방언과 같은 종속적 기능은 지원하지 않는다.
- 특정 DB만 지원하는 함수, 문법, SQL 쿼리 힌트
- 인라인 뷰(from절 서브쿼리), UNION, INTERSECT
- 스토어드 프로시저
종속적인 기능을 지원하는 방법은
- 특정 DB만 사용하는 함수
- 특정 JPQL에서 네이티브 SQL 함수를 호출할 수 있다.
- Hibernate는 DB 방언에 각 DB에 종속적 함수를 정의했다. 그리고 직접 호출할 함수를 정의하기도 가능하다.
- 특정 DB만 지원하는 힌트
- Hibernate를 포함한 몇몇 JPA구현체가 지원한다.
- 인라인 뷰(from절 서브쿼리), UNION, INTERSECT
- JPA구현체들이 지원한다. Hibernate는 지원 ❌
- 스토어드 프로시저
- JPQL에서 호출 가능
- 특정 DB만 지원하는 문법
- 너무 유니크한 쿼리 문법은 지원하지 않는데, 이때 네이티브 SQL사용.
근데 이걸 사용할까...?
- 너무 유니크한 쿼리 문법은 지원하지 않는데, 이때 네이티브 SQL사용.
네이티브 SQL을 사용하면 엔티티를 조회할 수 있고, JPA가 지원하는 영속성 컨텍스트의 기능을 그대로 사용할 수 있다.
네이티브 SQL 사용
네이티브 쿼리 API는 3가지가 있다.
- 결과 타입을 정의
- 결과 타입을 정의할수 없을 때
- 결과 매핑 사용
엔티티 조회
@Test
@DisplayName("네이티브 SQL 엔티티 조회")
void nativeQueryTest() {
String sql = "select id, name, price from item where price > ?";
Query nativeQuery = em.createNativeQuery(sql, Item.class).setParameter(1, 100);
List<Item> items = nativeQuery.getResultList();
}
jdbc 사용할때와 똑같은 느낌이 든다.
근데 가장 중요한점은
SQL만 직접 사용할 뿐, JPQL을 사용할 때와 같다. 조회한 엔티티도 영속성 컨텍스트에서 관리 된다.
값 조회
값으로 조회하려면 엔티티 조회처럼 class를 같이 넣어주는게 아니라em.createNativeQuery(sql)
를 사용하면 된다.
대신 이때 nativeQuery.getResultList()
는 Object 배열을 반환하므로List<Object[]>
로 반환을 받아야한다.
더욱 더 JDBC같이 생겼다.
결과 매핑 사용
결과 매핑을 사용하면 엔티티 자체에 너무 많은 어노테이션 설정을 해야되므로 보편적으로 사용하지 않을 것 같다는 나의 생각이 들어있다.
그래도 정리를 해보도록 하겠다.
String sql = "select M.ID, AGE, NAME, TEAM_ID, I.ORDER_COUNT FROM MEMBER M " +
"LEFT JOIN (SELECT IM.ID, COUNT(*) AS ORDER_COUNT FROM ORDERS O, MEMBER IM " +
"WHERE O.MEMBER_ID = IM.ID) I ON M.ID = I.ID";
Query nativeQuery = em.createNativeQuery(sql, "memberWithOrderCount");
List<Object[]> members = nativeQuery.getResultList();
아래는 매핑 정의 코드이다.
@Entity
@SqlResultSetMapping(name = "memberWithOrderCount",
entities = {@EntityResult(entityClass = Member.class) },
columns = {@ColumnResult(name = "ORDER_COUNT")}
}
public class Member {...}
id
, age
, name
, team_id
는 Member
엔티티로 매핑을 시키고 order_count
는 단순 칼럼으로 매핑했다.
이렇게 여러 컬럼들을 매핑해서 추출할 수 있다.
@NamedNativeQuery
Named 네이티브 SQL을 사용하여 정적 SQL도 작성이 가능하다.
엔티티 클래스에
@NamedNativeQuery(
name = "Member.memberSQL",
query = "select 조회 쿼리문",
resultClass = Member.class
)
로 등록해주고 사용하고자 하는 곳에서
TypedQuery<Member> nativeQuery = em.createNamedQuery("@NamedNativeQuery의 name", Member.class)
//파라미터가 있을 때
.setParameter();
네이티브 SQL은 휴먼에러를 발생할 확률이 QueryDSL보다 굉장히 높을 것으로 예상한다.
그래서 웬만하면 QueryDSL로 하지만 한방쿼리가 적절하게 필요할때만 사용하도록 해야할듯? 싶다. 😅
아직 실무에서 제대로 사용하지 않아서 이런 실무에서의 타협점은 점차 늘려가야 될것으로 보인다.