JPQL 은 특정 데이터베이스에 종속적인 기능은 지원하지 않는다
- 특정 데이터베이스만 지원하는 함수, 문법, SQL 쿼리 힌트
- 인라인 뷰(From 절에서 사용하는 서브쿼리), UNION, INTERSECT
- 스토어드 프로시저
다양한 이유로 JPQL을 사용할 수 없을 때 JPA는 SQL을 직접 사용할 수 있는 기능을 제공하는데 이것을 네이티브SQL이라고 한다
네이티브 SQL은 개발자가 직접 정의한느 것이다. 네이티브 SQL을 사용하면 엔티티를 조회할 수 있고 JPA가 지원하는 영속성 컨텍스트의 기능을 그대로 사용할 수 있다
10.5.1 네이티브 SQL 사용
String sql = "SELECT * FROM users WHERE status = :status";
List<User> users = entityManager.createNativeQuery(sql, User.class)
.setParameter("status", "ACTIVE")
.getResultList();
for (User user : users) {
System.out.println(user.getName());
}
- 엔티티 조회 코드
String sql = "SELECT name FROM users WHERE id = :id";
String userName = (String) entityManager.createNativeQuery(sql)
.setParameter("id", 1L)
.getSingleResult();
System.out.println("User Name: " + userName);
- 값 조회 코드
String sql = "SELECT id, name FROM users WHERE status = :status";
List<Object[]> results = entityManager.createNativeQuery(sql)
.setParameter("status", "ACTIVE")
.getResultList();
List<UserDTO> userDTOs = results.stream()
.map(row -> new UserDTO((Long) row[0], (String) row[1]))
.toList();
for (UserDTO dto : userDTOs) {
System.out.println(dto);
}
- 결과 매핑 사용 코드
10.5.4 네이티브 SQL 정리
- 네이티브 SQL은 JPQL이 자동 생성하는 SQL을 수동으로 직접 정리하는 것이다.
- 따라서 JPA가 제공하는 기능 대부분을 그대로 사용할 수 있다
- 네이티브 SQL은 관리하기 쉽지 않고 자주 사용하면 특정 데이터베이스에 종속적인 쿼리가 증가해서 이식성이 떨어진다
- 될 수 있으면 표준 JPQL을 사용하고 기능이 부족하면 차선책으로 하이버네이트 같은 JPA 구현체가 제공하는 기능을 사용하자
- 마지막 방법으로 네이티브 SQL을 사용하자!
10.6 객체지향 쿼리 심화
10.6.1 벌크 연산
엔티티를 수정하려면 영속성 컨텍스트의 변경 감지 기능이나 병합을 사용하고 삭제하려면 EntityManger.remove() 메서드를 사용한다. 하지만 이 방법으로 수백 개 이상의 엔티티를 하나씩 처리하기에는 시간이 너무 오래 걸린다. 이럴 때 여러 건을 한 번에 수정하거나 삭제하는 벌크 연산을 사용하면 된다
String qlString =
"update Prduct p " +
"set p.price = p.price * 1.1 " +
"where p.stockAmount < :stockAmount";
int resultCount = em.createQuery(qlString)
.setParameter("stockAmount", 10)
.executeUpdate();
- Update 벌크 연산
String qlString =
"delete from Product p " +
"where p.price <: price";
int resultCount = em.createQuery(qlString)
.setParameter("price", 100)
.executeUpdate();
- Delete 벌크 연산
벌크 연산의 주의점
- 벌크 연산은 영속성 컨텍스트를 무시하고 데이터베이스에 직접 쿼리한다는 점이다
해결법
- em.refresh() 사용
- 벌크 연산 먼저 실행
- 벌크 연산 수행 후 영속성 컨텍스트 초기화
10.6.2 영속성 컨텍스트와 JPQL
조회한 엔티티만 영속성 컨텍스트가 관리한다
JPQL로 조회한 엔티티와 영속성 컨텍스트
- JPQL로 데이터베이스에서 조회한 엔티티가 영속성 컨텍스트에 이미 있으면 JPQL로 데이터베이스에서 조회한 결과를 버리고 대신에 영속성 컨텍스트에 있던 엔티티를 반환한다
- 이 때 식별자 값을 사용해서 비교한다
10.6.3 JPQL과 플러시 모드
JPA는 플러시가 일어날 때 영속성 컨텍스트에 등록, 수정, 삭제 엔티티를 찾아 SQL 문을 만들어 데이터베이스에 반영한다
쿼리와 플러시 모드
em.setFlushMode(FlushModeType.AUTO); //커밋 또는 쿼리 실행시 플러시(기본값)
em.setFlushMode(FlushModeType.COMMIT); //커밋시에만 플러시
- JPQL은 영속성 컨텍스트에 데이터를 고려하지 않고 데이터베이스에 있는 데이터를 조회한다
- JPQL을 실행하기 전에 영속성 컨텍스트의 내용을 데이터베이스에 반영해야 한다
플러시 모드와 최적화
- FlushModeType.COMMIT 모드는 트랜잭션을 커밋할때만 플러시를 호출한다
- 잘못하면 무결성에 심각한 피해를 주지만, 플러시 횟수를 줄여 성능 최적화가 가능하다
'JPA 프로그래밍' 카테고리의 다른 글
Ch13. 웹 앱 영속성 관리 (0) | 2025.01.23 |
---|---|
Ch12. 스프링 데이터 JPA (0) | 2025.01.22 |
Ch10. 객체지향 쿼리 언어 (10.4 QueryDSL) (0) | 2025.01.21 |
Ch10. 객체지향 쿼리 언어 (10.3 Criteria) (1) | 2025.01.20 |
Ch10. 객체지향 쿼리 언어 (10.1 객체지향 쿼리 소개, 10.2 JPQL) (0) | 2025.01.20 |