일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- ORM
- java
- 다대다
- 프론트엔드
- 다대일
- JPA
- 플러시
- 바닐라js
- TODO
- 단방향
- 일대다
- 매핑
- 노마드코더
- SBERT
- 트랜잭션
- Django
- AWS
- css
- nomadcoder
- JS
- 장고독학
- web
- html
- python
- 영속성 컨텍스트
- clonecoding
- frontend
- 장고
- React
- javascript
- Today
- Total
꿈꾸는 새벽하늘
15장. 고급 주제와 성능 최적화 본문
1. 예외 처리
JPA 표준 예외
- 트랜잭션 롤백을 표시하는 예외
- javax.persistence.EntityExistsException
- javax.persistence.EntityNotFoundException
- javax.persistence.OptimisticLockException
- javax.persistence.PessimisticLockException
- javax.persistence.RollbackException
- javax.persistence.TransactionRequiredException
- 트랜잭션 롤백을 표시하지 않는 예외
- javax.persistence.NoResultException
- javax.persistence.NonUniqueResultException
- javax.persistence.LockTimeoutException
- javax.persistence.QueryTimeoutException
스프링 프레임워크가 제공하는 JPA 예외 변환 코드
@Repository
public class NoResultExceptionTestRepository {
@PersistenceContext EntityManager em;
public Member findMember() {
return em.createQuery("select m from Member m", Member.class).getSingleResult();
}
}
예외를 변환하지 않는 코드
@Repository
public class NoResultExceptionTestRepository {
@PersistenceContext EntityManager em;
public Member findMember() throws javax.persistence.NoResultException {
return em.createQuery("select m from Member m", Member.class).getSingleResult();
}
}
트랜잭션을 롤백하는 것은 데이터베이스의 반영사항만 롤백하는 것이지 수정한 자바 객체까지 원상태로 복구해주지는 않는다. 예를 들어 엔티티를 조회해서 수정하는 중에 문제가 있어서 트랜잭션을 롤백하면 데이터베이스의 데이터는 원래대로 복구되지만 객체는 수정된 상태로 영속성 컨텍스트에 남아 있다. 따라서 트랜잭션이 롤백된 영속성 컨텍스트를 그대로 사용하는 것은 위험하므로 새로운 영속성 컨텍스트를 생성해서 사용하거나 EntityManager.clear()를 호출해서 영속성 컨텍스트를 초기화한 다음에 사용해야 한다.
2. 엔티티 비교
같은 영속성 컨텍스트의 엔티티를 비교할 때는 동일성 비교를 할 수 있지만 영속성 컨텍스트가 다르면 동일성 비교에 실패한다. 따라서 자주 변하지 않는 비즈니스 키를 사용한 동등성 비교를 해야 한다.
3. 프록시 심화 주제
프록시를 사용하는 클라이언트는 조회한 엔티티가 프록시인지 아니면 원본 엔티티인지 구분하지 않고 사용할 수 있어야 한다. 하지만 프록시는 기술적인 한계가 있으므로 한계점을 인식하고 사용해야 한다.
4. 성능 최적화
1) N+1 문제
JPA로 애플리케이션을 개발할 때 성능상 가장 주의해야 하는 문제이다.
N+1 문제를 해결하는 가장 일반적인 방법은 페치 조인을 사용하는 것이다.
페치 조인은 SQL 조인을 사용해서 연관된 엔티티를 함께 조회하므로 N+1 문제가 발생하지 않는다.
select m from Member m join fetch m.orders
N+1 문제는 즉시 로딩이 아닌 지연 로딩만 사용하는 것을 추천한다. 즉시 로딩 전략은 비즈니스 로직에 따라 불필요한 엔티티를 로딩해야 하는 상황이 자주 발생하고 성능 최적화가 어렵기 때문이다.
2) 읽기 전용 쿼리의 성능 최적화
엔티티가 영속성 컨텍스트에 관리되면 1차 캐시부터 변경 감지까지 얻을 수 있는 혜택이 많다. 하지만 영속성 컨텍스트는 변경 감지를 위해 스냅샷 인스턴스를 보관하므로 더 많은 메모리를 사용하는 단점이 있다.
조회한 엔티티를 다시 조회할 일도 없고 수정할 일도 없이 딱 한 번만 읽어서 화면에 출력하면 된다면 읽기 전용으로 엔티티를 조회해서 메모리 사용량을 최적화할 수 있다.
다음 JPQL 쿼리를 최적화해보자.
select o from Order o
스칼라 타입으로 조회
엔티티가 아닌 스칼라 타입으로 모든 필드를 조회하는 것은 JPQL 쿼리를 최적화하는 가장 확실한 방법이다.
스칼라 타입은 영속성 컨텍스트가 결과를 관리하지 않는다.
select o.id, o.name, o.price from Order p
읽기 전용 쿼리 힌트 사용
하이버네이트 전용 힌트인 org.hibernate.readOnly를 사용하면 엔티티를 읽기 전용으로 조회할 수 있다.
읽기 전용이므로 영속성 컨텍스트는 스냅샷을 보관하지 않고, 따라서 메모리 사용량을 최적화할 수 있다.
단, 스냅샷이 없으므로 엔티티를 수정해도 데이터베이스에 반영되지 않는다.
TypeQuery<Order> query = em.createQuery("select o from Order o", Order.class);
query.setHint("org.hibernate.readOnly", true);
읽기 전용 트랜잭션 사용
스프링 프레임워크를 사용하면 트랜잭션을 읽기 전용 모드로 설정할 수 있다.
@Transactional (readOnly = true)
트랜잭션에 readOnly = true 옵션을 주면 스프링 프레임워크가 하이버네이트 세션의 플러시 모드를 MANUAL로 설정한다.
이렇게 하면 강제로 플러시를 호출하지 않는 한 플러시가 일어나지 않는다. 따라서 트랜잭션을 커밋해도 영속성 컨텍스트를 플러시하지 않는다. 영속성 컨텍스트를 플러시하지 않으니 엔티티의 등록, 수정, 삭제는 당연히 동작하지 않는다. 하지만 플러시할 때 일어나는 스냅샷 비교와 같은 무거운 로직들을 수행하지 않으므로 성능이 향상된다.
트랜잭션 밖에서 읽기
트랜잭션 밖에서 읽는다는 것은 트랜잭션 없이 엔티티를 조회한다는 뜻이다.
물론 JPA에서 데이터를 변경하려면 트랜잭션은 필수다. 따라서 조회가 목적일 때만 사용해야 한다.
스프링 프레임워크를 사용하면 다음처럼 설정한다.
@Transactional (propagation = Propagation.NOT_SUPPORTED)
J2EE 표준 컨테이너를 사용하면 다음처럼 설정한다.
@TransactionAttribute (TransactionAttributeType.NOT_SUPPORTED)
이렇게 트랜잭션을 사용하지 않으면 플러시가 일어나지 않으므로 조회 성능이 향상된다.
'🌿 Spring & Spring Boot > 📗 자바 ORM 표준 JPA 프로그래밍' 카테고리의 다른 글
16장. 트랜잭션과 락, 2차 캐시 (0) | 2023.09.11 |
---|---|
14장. 컬렉션과 부가 기능 (1) | 2023.08.28 |
13장. 웹 애플리케이션과 영속성 관리 (0) | 2023.08.21 |
12장. 스프링 데이터 JPA (0) | 2023.08.14 |
11장. 웹 애플리케이션 제작 (0) | 2023.08.07 |