김영한 님의 "자바 ORM 표준 JPA 프로그래밍" 책을 정리한 포스팅 입니다.
1. Native SQL
- JPA는 JPQL이 아닌 DB 고유의 SQL 문법을 직접 사용하는 방법을 지원합니다.
- 결과 매핑 어노테이션을 사용하여 응답을 정의할 수 있습니다.
사용 상황
상황 | 이유 |
복잡한 조인 / 서브쿼리 |
JPQL로 표현하기 어려운 경우
|
DB 특화 기능 사용 |
예: 윈도우 함수, 특정 DB 함수 등
|
성능 최적화 |
JPQL보다 효율적인 쿼리 필요 시
|
기존 SQL 자산 재사용 |
DB에 이미 작성된 쿼리를 그대로 쓰고 싶을 때
|
예제) Native SQL
더보기
@Test
@Transactional
void native_query_entity() {
String sql = "SELECT ID, NAME, NICKNAME, AGE, TEAM_ID FROM USER WHERE AGE > ?";
Query nativeQuery = entityManager.createNativeQuery(sql, User.class).setParameter(1, 20);
List<User> result = nativeQuery.getResultList();
for (User row : result) {
System.out.println(row);
}
}
- SELECT문에 누락된 컬럼이 있으면 예외 발생 ‼
- JPA는 Native SQL 사용 시, 엔티티 매핑을 위해 컬럼 명과 엔티티 필드를 1:1 매핑하려고 합니다.
@Test
@Transactional
void native_query_object() {
String sql = "SELECT ID, NAME, NICKNAME, AGE, TEAM_ID FROM USER WHERE AGE > ?";
Query nativeQuery = entityManager.createNativeQuery(sql).setParameter(1, 20);
List<Object[]> result = nativeQuery.getResultList();
for (Object[] row : result) {
System.out.println(row[0]);
System.out.println(row[1]);
System.out.println(row[2]);
System.out.println(row[3]);
}
}
- Object로 결과 받을수도 있음
2. Named Native Query
- Native Query를 사전에 정의합니다.
- 엔티티 클래스에 @NamedNativeQuery로 정의
- 간단한 쿼리일 경우, 직접 정의 가능
- 컴파일 시점에 정해지며, 재사용성이 높음
예제
더보기
@NamedNativeQuery(
name = "User.findAll",
query = "SELECT * FROM springboot_document.user",
resultClass = User.class
)
@Entity
@Getter
@Setter
@NoArgsConstructor
@Access(AccessType.FIELD)
public class User {
@Test
@Transactional
void native_query_result_class() {
Query nativeQuery = entityManager.createNamedQuery("User.findAll");
List<User> result = nativeQuery.getResultList();
for (User row : result) {
System.out.println(row);
}
}
결과 매핑 어노테이션
어노테이션 | 적용 대상 | 설명 |
@SqlResultSetMapping | 클래스 레벨 | 결과 매핑 정의를 모아놓는 컨테이너 역할 |
@EntityResult | @SqlResultSetMapping 내부 | 엔티티 매핑을 정의 (엔티티 클래스 지정) |
@FieldResult | @EntityResult 내부 | 결과 컬럼과 엔티티 필드 간 매핑 지정 |
@ColumnResult | @SqlResultSetMapping 내부 | 스칼라(단일 값) 타입 매핑 |
@ConstructorResult | @SqlResultSetMapping 내부 | DTO 생성자 기반 매핑 |
@NamedNativeQuery | 클래스 레벨 | SQL과 결과 매핑을 함께 명시 가능 |
예제) 결과 매핑 어노테이션
더보기
@SqlResultSetMapping(
name = "UserEntityAndDtoMapping",
entities = {
@EntityResult(
entityClass = User.class,
fields = {
@FieldResult(name = "id", column = "id"),
@FieldResult(name = "name", column = "name"),
@FieldResult(name = "nickname", column = "nickname"),
@FieldResult(name = "age", column = "age"),
@FieldResult(name = "team", column = "team_id")
}
)
},
columns = {
@ColumnResult(name = "id", type = Integer.class),
@ColumnResult(name = "name", type = String.class)
},
classes = {
@ConstructorResult(
targetClass = UserResponse.class,
columns = {
@ColumnResult(name = "id", type = Integer.class),
@ColumnResult(name = "name", type = String.class)
}
)
}
)
@NamedNativeQuery(
name = "User.findUserAndDto",
query = "SELECT id, name, nickname, age, team_id FROM springboot_document.user WHERE age > :minAge",
resultSetMapping = "UserEntityAndDtoMapping"
)
@Entity
@Getter
@Setter
@NoArgsConstructor
@Access(AccessType.FIELD)
public class User {
@Test
@Transactional
void native_query_result_mapping() {
Query nativeQuery = entityManager.createNamedQuery("User.findUserAndDto")
.setParameter("minAge", 20);
List<Object[]> result = nativeQuery.getResultList();
for (Object[] row : result) {
User user = (User) row[0];
UserResponse dto = (UserResponse) row[1];
System.out.println(user);
System.out.println(dto);
System.out.println();
}
}
3. 스토어드 프로시저
- @NamedStoredProcedureQuery 로 이름 지정 가능
- 복잡한 로직을 DB에 위임 가능
예제
더보기
CREATE PROCEDURE proc_multiply (INOUT inParam INT, INOUT outParam INT)
BEGIN
SET outParam = inParam * 2;
END;
@NamedStoredProcedureQuery(
name = "multiply",
procedureName = "proc_multiply",
parameters = {
@StoredProcedureParameter(name = "inParam", mode = ParameterMode.IN, type = Integer.class),
@StoredProcedureParameter(name = "outParam", mode = ParameterMode.OUT, type = Integer.class),
}
)
@Entity
@Getter
@Setter
@NoArgsConstructor
@Access(AccessType.FIELD)
public class User {
@Test
@Transactional
void native_query_stored_procedure_declaratively() {
StoredProcedureQuery spq = entityManager.createNamedStoredProcedureQuery("multiply");
spq.setParameter("inParam", 100);
spq.execute();
assertEquals((Integer) spq.getOutputParameterValue("outParam"), 200);
}
@Test
@Transactional
void native_query_stored_procedure_programmatibly() {
StoredProcedureQuery spq = entityManager.createStoredProcedureQuery("proc_multiply");
spq.registerStoredProcedureParameter(1, Integer.class, ParameterMode.IN);
spq.registerStoredProcedureParameter(2, Integer.class, ParameterMode.OUT);
spq.setParameter(1, 100);
spq.execute();
assertEquals((Integer) spq.getOutputParameterValue(2), 200);
}
'Spring > Spring Data JPA' 카테고리의 다른 글
[자바 ORM 표준 JPA 프로그래밍] 12. 스프링 데이터 JPA (0) | 2025.04.25 |
---|---|
[자바 ORM 표준 JPA 프로그래밍] 10-5. 객체지향 쿼리 언어: 객체지향 쿼리 심화 (0) | 2025.04.24 |
[자바 ORM 표준 JPA 프로그래밍] 10-2. 객체지향 쿼리 언어: Criteria (0) | 2025.04.24 |
[자바 ORM 표준 JPA 프로그래밍] 10-1. 객체지향 쿼리 언어: JPQL (0) | 2025.04.24 |
[Spring Data JPA] 3-4. Specifications (0) | 2024.08.03 |