Search

엔티티와 영속성 컨텍스트

Tags

개요

본 포스팅은 김영한님의 자바 ORM 표준 JPA 프로그래밍 - 기본편을 기반으로 작성되었습니다.

1. JPA를 사용하면서 반드시 알아야 할 것

JPA를 능숙하게 다루기 위해서는 다음 두 내용을 정확하게 파악하고 있어야 합니다.
 객체와 관계형 데이터베이스를 어떻게 매핑 하는가
데이터베이스를 설계하는 것 뿐만 아니라 객체를 어떻게 설계하여 JPA로 매핑 할 것인지 고민해야 합니다.
 영속성 컨텍스트를 이해하고 있는가
영속성 컨텍스트는 JPA의 내부 개념으로서 JPA가 어떻게 성능을 최적화 하고 동작 하는지 이해할 수 있습니다.
이번 포스팅에서는 영속성 컨텍스트에 대해 다뤄보도록 하겠습니다.

2. 영속성 컨텍스트란?

JPA를 온전히 이해하기 위해서는 영속성 컨텍스트를 이해해야합니다.
영속성 컨텍스트는 엔티티를 영구적으로 저장하는 환경이라는 뜻입니다.
우리가 어떤 엔티티를 저장하고자 할 때 사실은 영속성 컨테스트에 저장합니다.
EntityManager.persist(entity);
Java
복사
영속성 컨텍스트는 논리적인 개념으로서 그 실체가 없습니다. 우리는 엔티티 매니저를 통해서 영속성 컨텍스트에 접근합니다.
 아니, 엔티티 메니저가 뭔데?
영속성 컨텍스트에 대한 설명에 앞서 엔티티, 엔티티 매니저 팩토리 그리고 엔티티 매니저에 대해서 간략하게 짚고 넘어가겠습니다.

3. 엔티티

엔티티는 쉽게 말해서 JPA를 사용하기 위해서 관계형 데이터베이스에 대응되는 하나의 클래스입니다. 모든 JPA의 동작은 엔티티를 기준으로 돌아가게 됩니다.
간단한 예제 코드로 살펴보면 다음과 같습니다.
@Entity public class Person { private String name; private int age; }
Java
복사
spring-boot-starter-data-jpa 의존성을 추가한 뒤 @Entity을 붙이면 엔티티로 설정되며 테이블과 자바 클래스가 매핑됩니다.
 그렇다면 엔티티 매니저란 무엇일까요?
매니저라는 뜻 그대로 엔티티 객체들을 관리하는 역할을 합니다. 여기서 말하는 관리는 엔티티의 생명주기를 관리한다고 생각하면 쉽습니다.
즉, 엔티티 매니저는 관리하는 엔티티 객체들을 영속성 컨텍스트에 넣은 뒤 생명 주기를 관리하는 것이죠.
엔티티생명 주기는 다음과 같습니다.
New(비영속)
Java 영역에만 존재하고, 데이터베이스와 연동된 적 없는 상태.순수한 Java 객체로 엔티티 매니저가 관리하지 않음
Managed(영속)
DB에 저장되고, 메모리상에서도 같은 상태로 존재하는 상태.PK 값을 통해 필요한 엔티티 객체를 꺼내 사용할 수 있게 됩니다.
Removed(삭제)
DB 상에서 삭제된 상태. 객체는 영속 컨텍스트에 존재하지 않습니다.
Detached(준영속)
영속 컨텍스트에서 엔티티 객체를 꺼내서 사용하는 상태. 아직 DB와 동기화가 이루어지지 않은 상태
그림으로 보면 다음과 같습니다.
사실 위 글만 읽으면 도대체 엔티티와 엔티티 매니저가 어떤 일을 하는지 감이 안올 수 있습니다.
쉽게 말하면 다음과 같습니다.
영속성 컨텍스트이점을 활용하기 위해 관계형 데이터베이스에 저장되어 있는 엔티티 혹은 사용자가 넘긴 엔티티를 영속성 컨텍스트에 넣어 주는 것이 엔티티 매니저의 역할이다.
영속성 컨텍스트의 밑에 이어서 작성하겠습니다.
엔티티 매니저 팩토리엔티티 매니저를 관리하는 녀석이라고 생각하시면 됩니다.

4. 영속성 컨텍스트의 이점은

저는 영속성 컨텍스트를 객체와 관계형 데이터베이스 사이의 중간 다리 역할이라고 종종 말합니다.
영속성 컨텍스트의 이점은 다음과 같습니다.
1차 캐시
Person person = Person.builder() .id(1L); .name("건도리") .age(24) .build(); em.persist(person); Person gundorit = em.find(Person.class, 1L); ... // 추후에 다시 찾고자 했을 때 Person gundorit = em.find(Person.class, 1L);
Java
복사
캐시의 이점은 특정 객체를 찾고자 했을 때 영속성 컨텍스트가 해당 엔티티를 갖고 있으면 바로 반환 받을 수 있다는 점입니다. 즉, 매번 데이터베이스를 조회할 필요가 없어집니다.
동일성 보장
Person a = em.find(Person.class, 1L); Person b = em.find(Person.class, 1L); a==b // true
Java
복사
두 객체는 동일하다는 것을 보장합니다.
트랜잭션을 지원하는 쓰기 지연 (Transactional write-behind)
JPA는 트랜잭션 내에서 일어나는 영속성 컨텍스트에 대한 변경사항을 즉시 데이터베이스에 반영하지 않습니다. 영속성 컨텍스트 내의 엔티티 상태 변경은 트랜잭션의 커밋 시점에 한꺼번에 SQL로 데이터베이스에 반영됩니다. 이러한 방식은 네트워크 사용량을 최소화하고, 데이터베이스의 액세스를 줄여 성능을 향상시킬 수 있습니다.
transaction.begin(); person.setName("NewName"); // 이 시점에는 DB에 반영되지 않음 transaction.commit(); // 커밋 시점에 DB에 반영됨
Java
복사
변경 감지 (Dirty Checking)
JPA는 엔티티의 변경사항을 자동으로 감지하여 해당 변경사항만 데이터베이스에 반영합니다. 이를 변경 감지라고 부르며, 이 기능 덕분에 개발자는 변경된 필드만을 수동으로 추적하거나 저장할 필요가 없습니다.
Person person = em.find(Person.class, 1L); person.setName("UpdatedName"); // 엔티티의 상태 변경 transaction.commit(); // 변경된 상태만 데이터베이스에 반영됨
Java
복사
지연 로딩 (Lazy Loading)
JPA는 관계가 설정된 엔티티를 로딩할 때, 기본적으로 지연 로딩을 사용합니다. 이는 연관된 엔티티를 실제로 사용할 때까지 데이터베이스로부터 로딩을 지연시킨다는 의미입니다. 이 기능은 불필요한 데이터베이스 액세스를 줄여 성능을 향상시킵니다.
Order order = em.find(Order.class, 1L); List<Item> items = order.getItems(); // 이 시점에서 실제로 DB에서 아이템을 로딩함
Java
복사
이렇게 JPA는 데이터베이스 작업의 효율성과 애플리케이션 성능 향상을 위한 다양한 기능을 제공합니다.