[JPA] 고급 주제와 성능 최적화

들어가며

해당 글은 자바 ORM 표준 프로그래밍을 정리한 글입니다.

예외 처리

JPA 표준 예외들은 PersistenceException의 자식 클래스다. 그리고 이 예외 클래스는 RuntimeException의 자식이다. 따라서 JPA 예외는 모두 언체크 예외이다.

  • 트랜잭션 롤백을 표시하는 예외
    • 심각한 예외이므로 트랜잭션을 강제로 커밋해도 커밋되지 않고 롤백 된다.
  • 트랜잭션 롤백을 표시하지 않는 예외

트랜잭션 롤백 시 주의사항

트랜잭션 롤백하는 것은 데이터베이스의 반영사항만 롤백하는 것이지 수정한 자바 객체까지 원상태로 복구해주지 않는다. 따라서 영속성 컨텍스트를 생성해서 사용하거나 EntityManager.clear()를 호출해서 영속성 컨텍스트를 초기화한 다음에 사용해야 한다.

엔티티 비교

같은 영속성 컨텍스트에서 엔티티를 조회하면 항상 같은 엔티티 인스턴스를 반환한다. 주소값이 같은 인스턴스를 반환

영속성 컨텍스트가 같을 때 엔티티 비교

@Transaction의 propagation전략에 따라 다르지만 기본적으로 @Transaction에 시작에서부터 끝날 때 까지는 같은 영속성 컨텍스트를 공유 한다.

영속성 컨텍스트가 다를 때 엔티티 비교

엔티티 매니저가 서로 다른 영속성 컨텍스트를 가지므로 하나의 요청에 대해서 여러번의 데이터베이스 조회가 발생한다.

프록시 심화 주제

영속성 컨텍스트와 프록시

  • 프록시로 조회된 엔티티의 경우 em.find()로 조회 하더라도 프록시로 조회된다.
  • 영속성 컨텍스트에 값이 채워진 객체가 저장되어 있으면 프록시로 조회하더라도 영속성 컨텍스트에 있는 객체를 전달한다. 프록시라고 하더라도 식별자의 값은 갖고 있기에 영속성 컨텍스트의 동일성을 보장하기 위해 기존에 조회된 객체를 전달한다.

프록시 타입 비교

  • 프록시는 원본 엔티티를 상속 받아서 만들어지므로 프록시로 조회한 엔티티의 타입을 비교할 때는 == 비교가 아니라 instanceof를 사용해야 한다.

프록시 동등성 비교

  • 프록시의 경우 실제 데이터를 갖고 있지 않아 프록시의 멤버 변수에 직접 접근하면 아무값도 조회할 수 없다. 따라서 접근자(Getter)를 사용해야 원본 데이터를 조회 가능하다.

성능 최적화

N+1 문제

즉시 로딩과 N+1

  • JPQL로 조회시 select All하게 되면 연관관계 엔티티를 즉시 로딩하기 때문에 N+1번 조회가 발생한다.

지연 로딩과 N+1

  • 글로벌 페치 전략을 LAZY로 설정하더라도 실제로 사용할 때 1개씩 조회가 발생해서 N+1 조회가 발생한다.

페치 조인 사용

  • join fetch를 추가하여 조회하면 N+1 조회가 발생하지 않지만 해당 유저의 데이터가 너무 많은 경우 문제가 될 수 있다.

@BatchSize

  • 즉시 로딩시에 IN 절을 이용해서 미리 로드 해둔 뒤 다음 데이터를 사용하는 경우 다음 SQL을 추가로 실행한다.

읽기 전용 쿼리의 성능 최적화

스칼라 타입으로 조회

엔티티가 아닌 스칼라 타입으로 모든 필드를 조회하면 영속성 컨텍스트가 결과를 관리하지 않는다.

읽기 전용 쿼리 힌트 사용

읽기 전용이므로 영속성 컨텍스트는 스냅샷을 보관하지 않아 메모리 사용량을 최적화 할 수 있다. 단, 읽기 전용이므로 엔티티를 수정해도 데이터베이스에 반영되지 않는다.

읽기 전용 트랜잭션 사용

스프링 프레임워크를 사용하면 트랜잭션을 읽기 전용 모드로 설정할 수 있다. @Transaction(readOnly = true)로 설정하게 되면 강제로 플러시를 호출하지 않는 한 플러시가 일어나지 않는다.

배치 처리

영속성 컨텍스트에 엔티티가 계속 쌓이지 않도록 일정 단위마다 영속성 컨텍스트의 엔티티를 데이터베이스에 플러시하고 영속성 컨텍스트를 초기화해야 한다. 만약 이런 작업을 하지 않으면 영속성 컨텍스트에 너무 많은 엔티티가 저장되면서 메모리 부족 오류가 발생할 수 있다.

트랜잭션을 지원한 쓰기 지연과 성능 최적화

JPA에서 jdbc.batch_size값을 조정하여 한번에 insert 쿼리를 모아서 보낼 수 있다. IDENTITY 식별자 생성 전략에는 식별자 값을 얻어 오기 위해 INSERT가 즉시 실행되어 성능을 최적화 할 수 없다.



Leave a comment