15장 고급 주제와 성능 최적화

예외 처리

JPA 표준 예외들은 모두 런타임 오류 이다. 왜냐하면 JPA 표준 예외는 javax.persistence.PersistenceException 의 자식인데 이 클래스가 RuntimeException 의 자식이기 때문이다. 이 중에서도 2가지로 나누는데

  • 트랜잭션 롤백을 표시하는 예외

  • 트랜잭션 롤백을 표시하지 않는 예외

트랜잭션 롤백을 표시하는 예

설명

javax.persistence.EntityExistException

EntityManager.persist(...) 호출 시 이미 같은 엔티티가 있으면 발생한다.

java.persistence.EntityNotFoundException

EntityManager.getReference(...)를 호출했는데 실제 사용 시 엔티티가 존재하지 않으면 발생. refersh(...), lock(...)에서도 발생한다.

javax.persistence.OptimisticLockException

낙관적 락 충돌 시 발생한다.

javax.persistence.PessimisticLockException

비관적 락 충돌 시 발생한다.

javax.persistence.RollbackException EntityTransaction.commit()

실패 시 발생, 롤백이 표시되어 있는 트랜잭션 커밋 시에도 발생한다.

javax.persistence.TransactionRequiredException

트랜잭션이 필요할 때 트랜잭션이 없으면 발생. 트랜잭션 없이 엔티티를 변경할 때 주로 발생한다.

트랜잭션 롤백을 표시하지 않는 예

예외

설명

javax.persistence.NoResultException

Query.getSingleResult() 호출 시 결과가 하나도 없을 때 발생한다

javax.persistence.NonUniqueResultException

Query.getSingleResult() 호출 시 결과가 둘 이상일 때 발생한다.

javax.persistence.LockTimeoutException

비관적 락에서 시간 초과 시 발생한다

javax.persistence.QueryTimeoutException

쿼리 실행 시간 초과 시 발생한다.

JPA 예외를 스프링 예외로 변환

JPA 예외

스프링 변환 예외

javax.persistence.PersistenceException

org.springgramework.orm.jpa.JpaSystemException

javax.persistence.NoResultException

org.springfreamework.dao.ExptyResultDataAccessException

javax.persistence.NonUniqueResultException

org.springframework.dao.IncorrectrResultSizeDataAccessException

javax.persistence.LockTimeoutException

org.springframework.dao.CannotAcquireLockException

javax.persistence.QueryTimeoutException

org.springframework.dao.DataIntegrityViolationException

javax.persistence.EntityExistException

org.springframework.orm.jpa.JpaObjectRetrievalFailurewException

javax.persistence.EntityNotFoundException

org.springframework.orm.jpa.JpaObjectRetrivalFailtueException

javax.persistence.OptimisticLockException

org.springframework.dao.JpaOptimisticLockingFailureException

javax.persistence.PessimisticLockException

org.springframework.dao.PessimisticLockingFailureException

javax.persistence.TransactionRequiredException

org.springframework.dao.InvalidDataAccessApiUsageException

javax.persistence.RollbackException

org.springframework.transaction.TransactionSystemException

JPA 예외를 스프링 예외로 변경 추가

JPA 예외

스프링 변환 예외

java.lang.IllegalStateException

org.springframework.dao.InvalidDataAccessApiUsageException

java.lang.IllegalArgumentException

org.springframework.dao.InvalidDataAccessApiUsageException

트랜잭션 롤백 시 주의사항

트랜잭션을 롤백 한다는 것은 영속성컨텍스트 까지 롤백의 개념이 들어가는것이 아니다.

DB 반영 사항만 롤백하고 수정한 자바 객체의 상태를 되돌리지는 않는다는것이다.

영속성 컨텍스트에는 수정한 자바 객체의 상태가 그대로 남아있는것이다.

그렇기에 트랜잭션 롤백 후 em.clear 를 해서 영속성 컨텍스트도 깔끔하게 비워줘야 한다.

다만 기본전략인 트랜잭션이 같으면 영속성 컨텍스트도 동일하다 는 전략은 문제 발생시 트랜잭션 롤백 후 영속성 컨텍스트도 같이 종료 하기에 위와 같은 문제가 발생하지않는다.

필자는 기본전략이 기본전략인줄 모르고 당연히 트랜잭션은 영속성 컨텍스트는 같은 개념이라는 생각으로 프로젝트를 진행했었다..... 문제가 발생하지않은건 기본 전략 덕분...

문제는 OSIV 처럼 트랜잭션 당 영속성 컨텍스트가 하나가 아닌 하나의 영속성 콘텍스트가 여러트랜잭션을 공유하기에 위 같은 문제를 야기 할수있기에 항상 영속성 컨텍스트도 초기화 해야한다는걸 잊지말자!

엔티티 비교

영속성 컨텍스트를 통해 조회되거나 저장되어진 데이터는 1차 캐시에 담겨지게 된다.

1차 캐시에 담겨있다면 DB접근을 하지 않고 해당 엔티티를 사용할수있다.

그렇기에 이미 불러온 엔티티를 비교할때는 DB단과 비교하는게 아니므로 같은 영속성 컨텍스트를 영속되있는 엔티티에만 적용하는것이 옳다.

프록시 심화 주제

프록시는 실 엔티티를 상속받아 만들어지는 객체이다. 하지만 사용자 입장에서 프록시와 실 엔티티를 구분하는것 마냥 쉬운 일은 아니다. 그렇다면 만약 프록시와 실 엔티티의 구분을 짓지 못할때 어떤 문제점이 발생할까?

동등성 비교

엔티티의 동등성을 비교할땐 비즈니스 키를 이용해 equals() 메서드를 오버라이딩 해서 비교하면 된다. 하지만 IDE나 다른 라이브러리에서 제공하는 equals()를 사용한다면 원하는 결과를 얻지 못할 수 있다.

프록시는 엔티티를 상속받은 자식객체 이기에 단순 == , equlas 로 비교하면 fasle 값이 나올수있다.

그렇기에 instanceof() 메서드를 사용해서 비교해야 원하는 값을 내보낼수있다.

성능 최적화

JPA를 통해 애플리케이션을 개발할때 필요로하는 최적화 기능에 대해 알아보자

  1. N+1문제 최적화 방법

  • 페치조인

  • @BatchSize

  • @Fetch(FetchMode.SUBSELECT)

  • @Transactional(readOnly = true)

Last updated