햄발
Blog 프로젝트 만들기(JPA) - 영속성 컨텍스트 본문
영속성 컨텍스트 (Persistence Context)
엔티티 객체들을 관리하고 , 데이터베이스와의 상태를 동기화 하는 일종의 임시저장소 입니다.
애플리케이션이 실행되는동안 데이터베이스에서 가져온 엔티티 객체들을 임시로 보관해두는 메모리 공간 입니다.
이 공간에 있는 객체들은 [영속 상태] 로 관리되며, 이상태에 있는 동안 엔티티 객체의 변화는 자동으로 데이터 베이스에 반영됩니다.
즉, 이것이 있기 때문에 데이터베이스에서 효과적으로 데이터를 가져올 수 있고, 엔티티를 편하게 사용할 수 있습니다.
핵심 키워드
영속성 컨텍스트
엔티티 객체를 관리하고, 데이터 베이스와 동기화하는 임시저장소
영속 상태
영속성 컨텍스트에 의해 관리되는 상태, 데이터 베이스와의 동기화가 이루어짐
추가 설명!
영속성 컨텍스트는 엔티티매니저가 활용, 관리하며 그 대상은 엔티티가 됩니다.
엔티티 매니저와 영속성 컨텍스트의 관계
엔티티 매니저
- 엔티티 매니저는 JPA 에서 제공하는 주요 인터페이스로 , 영속성 컨텍스트를 통해 엔티티의 생명주기를 관리합니다.
- 엔티티 매니저는 엔티티를 생성 , 조회, 수정, 삭제 하는 작업을 수행하며, 이 모든 작업은 영속성 컨텍스트 내에서 이루어집니다.
영속성 컨텍스트 (Persistence Context)
- 엔티티 매니저가 관리하는 엔티티 객체들을 저장하고 관리하는 공간입니다.
- 이 컨텍스트는 데이터베이스와 동기화되며, 엔티티 객체의 상태를 추적합니다.
- 엔티티의 속성이 변경되면 이 변경 사항은 영속성 컨텍스트에 반영되고, 트랜잭션이 완료되면 데이터베이스에 자동으로 반영됩니다.
엔티티
- 영속성 컨텍스트에 의해 관리되는 자바 객체입니다.
- 엔티티가 영속성 컨텍스트에 포함될 때, 이를 영속 상태 라고 합니다.
이 상태에서 엔티티는 데이터베이스와 연결된 상태이며, 변경사항이 자동으로 데이터베이스에 동기화됩니다.
영속성 컨텍스트에서
엔티티는 4가지 상태를 가질수 있습니다.
비영속 상태
엔티티가 영속성 컨텍스트와 전혀 연관되지않은 상태입니다.
단순히 자바객체로만 존재하며, 데이터베이스와 연결되지 않습니다.
User user = new User("홍길동", "hong@example.com");
영속상태
엔티티가 영속성 컨텍스트에 의해 관리되는 상태 입니다.
이 상태에선 엔티티가 데이터 베이스의 레코드와 맵핑되어있으며, 엔티티의 상태 변화가 자동으로 데이터 베이스에 반영된다.
엔티티 매니저는 이상태의 엔티티를 추적하고, 트랜잭션이 종료될때 데이터베이스에 자동으로 변경사항을 반영합니다.
EntityManager em = entityManagerFactory.createEntityManager();
em.getTransaction().begin();
User user = new User("김키티", "hong@example.com");
em.persist(user); // 영속성 컨텍스트에 user 객체를 저장
em.getTransaction().commit();
준영속 상태 (Detached State)
엔티티가 한번 영속상태였지만, 더이상 영속성 컨텍스트에 의해 관리되지않는 상태입니다.
이 상태에서는엔티티 매니저가 더 이상 해당 객체를 추적하지 않으며, 변경 사항도 데이터베이스에 반영되지않습니다.
이 상태로 전환되는 경우는 EntityManager.clear(), EntityManager.detach(), 또는 EntityManager.close() 등을 호출할 때입니다.
User user = em.find(User.class, 1L); // 영속 상태
em.detach(user); // 준영속 상태로 전환
user.setName("김키티"); // 데이터베이스에 반영되지 않음
삭제상태 (Removed State)
엔티티가 영속성 컨텍스트에 의해 관리되지만 , 삭제가 예약된 상태입니다.
트랜잭션이 커밋된다면 해당 엔티티는 데이터 베이스에서 삭제됩니다.
이 상태로 전환된 엔티티는 데이터베이스에 더이상 존재하지않으며 , 영속성 컨텍스트에서도 제거 됩니다.
트랜잭션이 커밋될때 데이터베이스에서 해당 레코드가 삭제
User user = em.find(User.class, 1L); // 영속 상태
em.remove(user); // 삭제 상태로 전환
영속성 컨텍스트의 기본적인 특징
1차 캐시 (First-Level Cache)
영속성 컨텐스트는 내부에 1차 캐시를 가지고있습니다.
이때 캐시의 키는 엔티티의 @Id 어노테이션이 달린 기본 키 역할을 하는 식별자 이며 , 값은 엔티티입니다.
엔티티를 조회하면 1차 캐시에서 데이터를 조회하고 값이 있으면 반환합니다.
값이없으면 데이터베이스에서 조회해 1차 캐시를 저장한 다음 반환합니다.
이를 통해 데이터를 조회할 때 데이터베이스를 거치지 않아도 되므로, 빠르게 데이터를 조회할 수 있습니다.
쓰기 지연 (Write-Delay)
쓰기 지연은 영속성 컨텍스트가 데이터베이스에 직접적으로 즉시 쿼리를 보내지않고, 트랜잭션이 커밋될 때 까지 쿼리들을 모아서 처리하는 기법입니다. 이를통해 성능을 최적화하고, 트랜잭션 내에서 데이터의 일관성을 유지하는데 중요한 역할을 합니다.
성능 최적화
SQL 쿼리를 한번에 모아서 전송하기 때문에 , 네트워크 통신 비용이 줄어들고 , 데이터베이스 입출력 횟수를 줄여서 성능이 향상됩니다.
일관성 유지
트랜잭션이 끝날때 까지 데이터 베이스에 반영되지않으므로, 트랜잭션 내에서 데이터 일관성을 유지할 수 있습니다.
변경감지 (Dirty Checking)
트랜잭션을 커밋하면 1차 캐시에 저장되어 있는 엔티티의 값과 현재 엔티티의 값을 비교해서 변경된 값이 있다면 변경 사항을 감지해 변경된 값을 데이터베이스에 자동으로 반영합니다. 이를 통해 쓰기 지연과 마찬가지로 적당한 묶음으로 쿼리를 요청할 수 있고, 시스템의 부담을 줄일 수 있습니다.
실행 흐름 요약
트랜잭션을 커밋할 때, JPA는 변경 감지를 통해 1차 캐시에 있는 엔티티와 현재 엔티티의 상태를 비교합니다.
변경 사항이 있으면, 해당 엔티티에 대해 UPDATE 쿼리를 생성하고, 이를 트랜잭션 커밋 시점에 데이터베이스에 반영합니다.
이를 통해, 쓰기 지연과 결합하여 효율적으로 쿼리를 처리하고 시스템 부담을 줄일 수 있습니다.
지연로딩(Lazy Loading)
지연로딩은 쿼리로 요청한 데이터를 애플리케이션에서 바로 로딩하는 것이 아니라 필요할 때 쿼리를 날려 데이터를 조회하는 것을 의미합니다.
@OneToMany(fetch = FetchType.LAZY)
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(fetch = FetchType.LAZY)
private List<Order> orders;
// getters and setters
}
- 객체 초기화 시점
- JPA에서 엔티티를 조회할 때, 연관된 다른 엔티티나 컬렉션에 대해 실제 데이터를 즉시 로드하지 않고, 해당 객체에 대한 프록시 객체(가짜 객체)를 대신 반환합니다. @OneToMany(fetch = FetchType.LAZY) private List<Order> orders;
- 이 프록시 객체는 실제 데이터베이스 쿼리가 발생할 때까지 데이터를 불러오지 않습니다.
- JPA에서 엔티티를 조회할 때, 연관된 다른 엔티티나 컬렉션에 대해 실제 데이터를 즉시 로드하지 않고, 해당 객체에 대한 프록시 객체(가짜 객체)를 대신 반환합니다. @OneToMany(fetch = FetchType.LAZY) private List<Order> orders;
- 필요할 때 데이터 조회
- 연관된 객체나 컬렉션의 데이터가 실제로 접근되는 시점(예: getter 메서드 호출 등)에 데이터베이스에서 쿼리가 실행되어 필요한 데이터를 가져옵니다.
- 성능 최적화
- 지연 로딩을 통해 초기화 시점에서 불필요한 데이터베이스 접근을 방지하고, 메모리 사용량을 줄일 수 있습니다.
이는 성능을 최적화하는 데 중요한 역할을 합니다.
- 지연 로딩을 통해 초기화 시점에서 불필요한 데이터베이스 접근을 방지하고, 메모리 사용량을 줄일 수 있습니다.
즉시 로딩 (Eager Loading)
지연 로딩(Lazy Loading)의 반대 개념은 즉지 로딩이란 개념도 있습니다.
엔티티를 조회할 때, 연관된 모든 데이터도 함께 즉시 로드하는 방식입니다.
즉, 엔티티가 처음 조회되는 시점에 그와 관련된 모든 연관 엔티티들도 한꺼번에 데이터베이스에서 가져옵니다.
@OneToMany(fetch = FetchType.EAGER) // 즉시 로딩 설정
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(fetch = FetchType.EAGER) // 즉시 로딩 설정
private List<Order> orders;
// getters and setters
}
- 초기화 시점:
- 엔티티가 처음 로드될 때 연관된 모든 엔티티나 컬렉션도 함께 로드됩니다.
- 이로 인해 데이터베이스에 필요한 모든 쿼리가 한 번에 실행되며, 추가적인 데이터베이스 접근이 필요 없습니다.
- 성능:
- 연관된 데이터가 많을 경우, 처음 로딩 시점에서 많은 데이터를 가져오게 되므로 초기 로딩 시간이 길어질 수 있습니다.
- 하지만 이후에는 추가적인 데이터베이스 접근이 필요 없기 때문에, 이후의 접근 속도가 빨라지는 장점이 있습니다.
'Spring boot' 카테고리의 다른 글
Blog 프로젝트 만들기(JPA) - 엔티티 매니저 (0) | 2024.10.01 |
---|---|
Blog 프로젝트 만들기(JPA) - JPA 와 하이버네이트 (2) | 2024.09.30 |
Blog 프로젝트 만들기(JPA) - 스프링 부트 DB 접근 기술 ORM (0) | 2024.09.30 |
Blog 프로젝트 만들기(JPA) - DELETE 방식에 이해 및 실습 (0) | 2024.09.28 |
Blog 프로젝트 만들기(JPA) - PUT 방식에 이해 및 실습 (0) | 2024.09.28 |