You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
영속성(Persistence)은 데이터베이스와 어플리케이션 객체 간의 상태를 관리하는 개념
영속성 컨텍스트(Persistence Context)는 엔티티 객체의 생명주기를 관리하고 엔티티 객체는 이 컨텍스트 내에서 데이터베이스와 동기화 됨
영속성 컨텍스트는 1차 캐시를 제공하고, 1차 캐시는 1차 캐시는 동일한 트랜잭션 내에서 엔티티 매니저가 관리하는 엔티티 객체를 메모리에 저장함
이를 통해 같은 엔티티를 여러 번 조회할 때 데이터베이스에 반복적으로 접근하지 않아도 됨
EntityManagerem = entityManagerFactory.createEntityManager();
em.getTransaction().begin();
Personperson = em.find(Person.class, 1L); // 첫 번째 조회, DB 접근PersonsamePerson = em.find(Person.class, 1L); // 두 번째 조회, 1차 캐시 사용em.getTransaction().commit();
em.close();
영속성 컨텍스트는 트랜잭션이 끝날 때 까지 엔티티를 추적함. 변경 감지 (Dirty Checking)를 통해 트랜잭션이 종료된 경우 변경된 부분만 데이터베이스에 반영함
영속성 컨텍스트의 객체는 지연 로딩 (Lazy Loading)을 통해 엔티티 객체의 연관된 데이터는 실제로 필요할 때 로드됨. 이는 초기 데이터베이스 조회 시 불필요한 데이터를 가져오지 않도록 하여 성능을 최적화함
JPA는 데이터베이스에 대한 쓰기 작업을 트랜잭션 종료 시점까지 지연하는 쓰기 지연 (Write-behind)를 통해 여러 쓰기 작업을 하나의 배치로 처리하여 성능을 향상시킴
JPA 구현체(Hibernate)는 2차 캐시를 지원하여 2차 캐시는 영속성 컨텍스트를 벗어난 데이터도 캐싱하여 데이터베이스 부하를 줄임
2. N + 1 문제란?
JPA의 N + 1 문제는 단일 쿼리로 다수의 엔티티를 조회한 후 각 엔티티의 연관된 엔티티를 개별 쿼리로 조회하는 과정에서 발생하는 성능 문제
JPA에서 흔히 발생하는 성능 이슈 중 하나로 하나의 쿼리를 통해 특정 엔티티 리스트를 조회한 후, 각 엔티티의 연관된 엔티티를 조회할 때 추가적인 쿼리가 발생하여 총 N+1 개의 쿼리가 실행되는 문제를 의미
@EntitypublicclassOrder {
@Id@GeneratedValue(strategy = GenerationType.IDENTITY)
privateLongid;
@ManyToOne(fetch = FetchType.LAZY)
privateCustomercustomer;
}
@EntitypublicclassCustomer {
@Id@GeneratedValue(strategy = GenerationType.IDENTITY)
privateLongid;
privateStringname;
}
// 모든 주문을 조회하고 각 주문에 연관된 고객을 출력한다고 가정할 때/* select o from order o 모든 주문을 조회하는 하나의 쿼리 실행시 각 주문에 대해 order.getCustomer().getName() 을 호출하여 고객 정보를 조회하는 N개의 쿼리가 실행되어 총 N+1 개의 쿼리가 실행됨*/List<Order> orders = em.createQuery("SELECT o FROM Order o", Order.class).getResultList();
for (Orderorder : orders) {
System.out.println(order.getCustomer().getName());
}
이를 해결하기 위한 방법은 다음과 같음
즉시 로딩 (Eager Loading) 사용
연관된 엔티티를 즉시 로딩하도록 설정하여 단일 쿼리로 모든 데이터를 가져옴
'fetch = FetchType.EAGER'로 설정하면 Order를 조회할 때 Customer도 함께 조회함
하지만 즉시 로딩을 남용하면 불필요한 데이터까지 로딩하게 되어 다른 성능 문제가 발생할 수 있어 따라서 신중하게 사용이 필요