반응형
이번 포스트에서는 Spring Data JPA의 영속성에 대해서 알아보자
1. 영속성이란?
- JPA의 영속성(Persistence)은 데이터를 생성한 프로그램의 실행이 종료되더라도 사라지지 않는 데이터의 특성을 의미한다.
- 여기서 말하는 데이터는 주로 데이터베이스에 저장되는 데이터를 말한다. JPA에서는 이 영속성을 다루기 위해 여러 개념과 기능을 제공하는데, 그 중에서 가장 중요한 것이 '엔티티의 생명주기'와 '영속성 컨텍스트'이다.
2. 엔티티의 생명주기
- JPA에서 엔티티는 특정한 생명주기를 가지며, 이 생명주기는 엔티티의 상태에 따라 변화한다.
- 엔티티의 상태는 크게 '비영속 (new/transient)', '영속 (managed)', '준영속 (detached)', '삭제 (removed)'의 4가지 상태로 나눠진다.
- 비영속 (new/transient):
- 아직 영속성 컨텍스트에 저장되지 않은 새로운 상태의 엔티티를 말한다.
- 아직 영속성 컨텍스트에 저장되지 않은 새로운 상태의 엔티티를 말한다.
- 영속 (managed):
- 영속성 컨텍스트에 저장된 상태의 엔티티를 말한다. 이 상태의 엔티티는 데이터베이스에 반영되며, 영속성 컨텍스트가 관리하는 범위 안에 있다.
- 영속성 컨텍스트에 저장된 상태의 엔티티를 말한다. 이 상태의 엔티티는 데이터베이스에 반영되며, 영속성 컨텍스트가 관리하는 범위 안에 있다.
- 준영속 (detached):
- 영속성 컨텍스트에 저장되었다가 분리된 상태의 엔티티를 말한다. 이 상태의 엔티티는 더 이상 영속성 컨텍스트에 의해 관리되지 않는다.
- 영속성 컨텍스트에 저장되었다가 분리된 상태의 엔티티를 말한다. 이 상태의 엔티티는 더 이상 영속성 컨텍스트에 의해 관리되지 않는다.
- 삭제 (removed):
- 영속성 컨텍스트에 의해 관리되고 있지만, 삭제하기로 한 상태의 엔티티를 말한다. 이 상태의 엔티티는 트랜잭션 커밋 시점에 데이터베이스에서 삭제된다.
- 영속성 컨텍스트에 의해 관리되고 있지만, 삭제하기로 한 상태의 엔티티를 말한다. 이 상태의 엔티티는 트랜잭션 커밋 시점에 데이터베이스에서 삭제된다.
- 비영속 (new/transient):
3. 영속성 컨텍스트
- 영속성 컨텍스트(Persistence Context)는 JPA에서 매우 중요한 역할을 하는 개념으로, '엔티티를 영구 저장하는 환경'이라는 뜻이다. 이는 엔티티가 영속화되면서 생명주기를 관리하며, 엔티티의 CRUD 작업을 처리한다.
- 영속성 컨텍스트는 EntityManager 인스턴스를 생성할 때 하나씩 만들어지며, 이 EntityManager를 통해 엔티티를 데이터베이스에 저장하거나 검색할 수 있다.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("persistence-unit");
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
// 영속 상태
Member member = new Member();
member.setId("ID1");
member.setName("홍길동");
em.persist(member);
em.getTransaction().commit();
- 위의 코드에서 em.persist(member); 라는 메서드가 호출되면, member 엔티티는 영속성 컨텍스트에 의해 관리되는 영속 상태가 된다. 이후 커밋(commit)을 하면, 해당 변경 사항은 데이터베이스에 반영된다.
- 영속성 컨텍스트는 이 외에도 다양한 이점을 제공하는데, 대표적으로 1차 캐시, 동일성 보장, 트랜잭션을 지원하는 쓰기 지연, 변경 감지(Dirty Checking), 지연 로딩(Lazy Loading) 등의 기능을 제공한다. 이러한 기능들 덕분에 개발자는 데이터베이스에 직접 접근하여 작업을 처리하는 것보다 훨씬 효과적이고 안전하게 데이터를 관리할 수 있다.
4. 영속성 컨텍스트 사용의 이점
- 1차 캐시:
- 영속성 컨텍스트는 내부에 1차 캐시를 가지고 있다.
- 1차 캐시는 Map<엔티티ID, 엔티티 인스턴스> 즉, 엔티티의 ID를 key로, 엔티티 인스턴스를 값으로 가지는 맵으로 구현되어 있다. 이를 통해 같은 트랜잭션에서 동일한 엔티티를 조회할 경우에는 데이터베이스에 접근하지 않고 1차 캐시에서 조회하여 성능 향상을 이뤄낸다.
- 동일성 보장:
- 같은 트랜잭션 내에서는 영속성 컨텍스트가 1차 캐시를 통해 동일성(==)을 보장한다.
- 즉, 같은 엔티티 인스턴스에 대한 비교는 항상 true를 반환한다.
- 트랜잭션을 지원하는 쓰기 지연 (Write-Behind):
- 영속성 컨텍스트는 트랜잭션을 커밋하기 전까지는 데이터베이스에 엔티티를 저장하지 않고, 커밋하는 순간에 한꺼번에 SQL을 실행한다. 이를 통해 네트워크 비용 최소화와 성능 향상을 이룰 수 있다.
- 영속성 컨텍스트는 트랜잭션을 커밋하기 전까지는 데이터베이스에 엔티티를 저장하지 않고, 커밋하는 순간에 한꺼번에 SQL을 실행한다. 이를 통해 네트워크 비용 최소화와 성능 향상을 이룰 수 있다.
- 변경 감지 (Dirty Checking):
- 영속 상태의 엔티티에 대한 변경사항은 트랜잭션 커밋 시점에 자동으로 데이터베이스에 반영된다.
- 개발자는 엔티티의 변경만 진행하면 되며, 별도의 업데이트 쿼리를 작성할 필요가 없다.
- 지연 로딩 (Lazy Loading):
- 연관된 엔티티를 로딩할 때, 실제로 그 값을 사용하는 시점까지 로딩을 지연시키는 기능이다. 이를 통해 성능 최적화를 이루어낸다.
- 연관된 엔티티를 로딩할 때, 실제로 그 값을 사용하는 시점까지 로딩을 지연시키는 기능이다. 이를 통해 성능 최적화를 이루어낸다.
이러한 기능들은 JPA의 영속성 컨텍스트가 제공하는 중요한 기능들이며, 이를 이해하고 잘 활용하면 애플리케이션의 성능과 코드의 품질을 크게 향상시킬 수 있다.
5. EntityManager와 영속성 컨텍스트
- JPA의 영속성은 "EntityManager"를 중심으로 구성된다. EntityManager는 영속성 컨텍스트에 엔티티를 저장하고 관리하는 역할을 한다.
- EntityManager 인스턴스는 클라이언트에게서 요청이 올 때마다 생성되며, 그 요청이 끝나면 종료된다. 그래서 일반적으로는EntityManager 인스턴스와 같은 수명 주기를 가지게 된다.
// EntityManager 생성
EntityManager em = emf.createEntityManager();
// 엔티티 영속화
Member member = new Member("id1", "John Doe");
em.persist(member);
// EntityManager 종료 (영속성 컨텍스트도 종료됨)
em.close();
- EntityManager를 생성하면 자동으로 영속성 컨텍스트도 생성된다. 그리고 이 영속성 컨텍스트는 EntityManager가 종료될 때까지 유지된다. EntityManager의 persist() 메서드를 통해 엔티티를 영속성 컨텍스트에 저장하면, 그 엔티티는 '영속 상태'가 된다.
6. 코드를 통한 EntityManager의 사용예시
- 이 예시코드에서는 persist(), flush(), clear(), begin(), commit(), rollback(), close()와 같은 메서드들이 사용되었다.
- 이들 메서드는 JPA의 주요 메서드들로, 각각 엔티티의 영속화, 영속성 컨텍스트의 초기화, 트랜잭션의 시작/커밋/롤백, EntityManager 및 EntityManagerFactory의 자원 해제 등에 사용된다.
public class JpaMain {
public static void main(String[] args) {
// EntityManagerFactory를 생성. persistence unit 설정에 정의된 데이터베이스 연결 등을 관리.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
// EntityManager 생성. 영속성 컨텍스트가 생성되며 엔티티를 관리 시작.
EntityManager em = emf.createEntityManager();
// 트랜잭션 인스턴스를 가져온다. 데이터베이스 연산은 트랜잭션 내에서 이루어져야 함.
EntityTransaction tx = em.getTransaction();
// 트랜잭션 시작
tx.begin();
try {
// Order 인스턴스 생성
Order order = new Order();
// OrderItem 인스턴스 생성 후 order를 설정
OrderItem orderItem = new OrderItem();
orderItem.setOrder(order);
// orderItem 영속화
em.persist(orderItem);
// 현재 영속성 컨텍스트에 있는 변경 내용을 데이터베이스에 반영
em.flush();
// 영속성 컨텍스트 초기화
em.clear();
// 다른 업무 로직 실행...
// 트랜잭션 커밋. 변경 내용을 데이터베이스에 반영.
tx.commit();
} catch (Exception e) {
// 예외 발생 시, 롤백.
tx.rollback();
} finally {
// EntityManager 자원 해제. 내부의 영속성 컨텍스트도 종료됨.
em.close();
}
// EntityManagerFactory 자원 해제.
emf.close();
}
}
2023.08.13 - [JPA] - Spring JPA - Entity
반응형
'Spring Data JPA' 카테고리의 다른 글
Spring JPA - 엔티티를 DTO로 바꿔서 사용하는 이유 (0) | 2023.08.13 |
---|---|
Spring JPA - 연관관계 매핑 (0) | 2023.08.13 |
Spring JPA - Entity (0) | 2023.08.13 |
Spring JPA [SpringBoot3.1] - Querydsl 사용 (0) | 2023.08.08 |
Spring JPA [SpringBoot3.x.x] - Querydsl 적용하고 빌드하기 (0) | 2023.08.08 |