일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 |
- piplining
- 프로그래머스
- 완전탐색
- 구현
- 결제서비스
- 크롤링
- 누적합
- spring event
- next-stock
- 검색어 추천
- JPA
- 이분탐색
- 아키텍쳐 개선
- ipo 매매자동화
- 카카오
- AWS
- ai agent
- 셀러리
- jwt 표준
- 레디스 동시성
- BFS
- langgraph
- 백준
- 추천 검색 기능
- 디버깅
- 트랜잭샨
- gRPC
- docker
- 몽고 인덱스
- 쿠키
- Today
- Total
코딩관계론
스프링 JPA 영속성 컨텍스트란? (EntityManger, Entity 생명주기, 1차 캐시, 쓰기 지연, dirty checking, flush) 본문
스프링 JPA 영속성 컨텍스트란? (EntityManger, Entity 생명주기, 1차 캐시, 쓰기 지연, dirty checking, flush)
개발자_티모 2024. 7. 22. 15:42영속성 컨텍스트란
영속성 컨텍스트는 논리적인 개념으로, 영속성 엔티티들의 집합을 의미합니다. JPA에서는 EntityManager를 통해 영속성 엔티티들이 데이터베이스에 반영됩니다.
영속성 컨텍스트를 사용하는 이유는 1차 캐시 ,동일성 보장, 트랜잭션, 변경 감지 , 지연 로딩 장점을 가질 수 있기 때문입니다. 이런 장점들에 의해서 객체 관리가 쉬워지고, DB 로직이 트렌젝션 단위로 실행되게 됩니다.
Entity와 EntityManager란
- Entity: 테이블에 매핑되는 자바 클래스를 의미합니다.
- EntityManager: JPA에서 영속성 컨텍스트를 관리하며, 영속성 컨텍스트에 있는 엔티티들을 데이터베이스에 저장해주는 역할을 합니다. EntityManager는 엔티티의 생명주기를 관리하고, 트랜잭션을 통해 변경 사항을 데이터베이스에 반영합니다. 이러한 EntityManager와 영속 컨텍스트는 1대1로 매칭되게 됩니다.
1차 캐시란?
1차 캐시는 영속성 컨텍스트에 데이터베이스에서 불러온 데이터를 저장하여, 이후 재조회 시 영속성 컨텍스트에 있는 데이터를 반환하는 기능을 의미합니다. 이를 통해 데이터베이스 접근을 최소화하고 성능을 최적화할 수 있습니다.
1차 캐시의 동작 방식
- 데이터 조회 시:
- 처음 데이터를 조회할 때, 데이터가 1차 캐시에 없으면 데이터베이스에 쿼리를 전송해 데이터를 불러옵니다.
- 불러온 데이터는 영속성 컨텍스트에 저장됩니다.
- 이후 동일한 데이터를 조회하면, 1차 캐시에서 데이터를 찾아 반환합니다. 데이터베이스에 다시 쿼리를 전송하지 않습니다.
- 데이터 재조회 시
- 1차 캐시에 데이터가 존재하므로 데이터베이스에 쿼리를 보내지 않고, 1차 캐시에서 데이터를 반환합니다.
아래와 같은 엔티티와 코드가 있다고 했을 때 동장 방식은 아래의 그림과 같습니다.
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
@Entity
public class Member {
@Id
private Long id;
private String name;
}
// JPA 사용 예제
public class JpaExample {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("myPersistenceUnit");
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
// 첫 번째 조회 - DB에서 데이터를 불러와 1차 캐시에 저장
Member entity1 = em.find(Member.class, 1L);
// 두 번째 조회 - 1차 캐시에서 데이터를 가져옴
Member entity2 = em.find(Member.class, 1L);
em.getTransaction().commit();
em.close();
emf.close();
}
}
동일성 보장
JPA에서 관리하는 객체들은 동일성이 보장됩니다. 그 이유는 JPA에서 관리되는 모든 객체들은 영속 컨택스트에서 관리되고 있고, 이러한 값을 불러오는 것은 1차 캐시에 저장되어 있는 엔티티를 불러오는 방식임으로 주소값이 달라지지 않기 때문입니다.
동일성이 보장되는 것이 장점인 이유는 아래와 같이 요약할 수 있습니다:
- 변경 사항의 일관성:
- 동일한 객체를 여러 곳에서 수정할 때, 모든 수정 사항이 일관되게 반영됩니다. 이는 데이터의 신뢰성을 높여줍니다.
- 트랜잭션 범위 내에서 동일한 객체:
- 트랜잭션 내에서 동일한 엔티티 인스턴스를 사용하는 것은 트랜잭션의 일관성을 보장합니다. 이는 트랜잭션 롤백 시에도 일관된 상태를 유지하는 데 도움이 됩니다.
- 변경 감지와 자동 반영:
- 동일한 엔티티 인스턴스를 사용함으로써 변경 감지가 일관되게 작동하며, 트랜잭션 커밋 시 자동으로 변경 사항이 데이터베이스에 반영됩니다.
변경 감지(Dirty Checking)
JPA에서는 엔티티의 변경 사항을 자동으로 감지하고, 이를 데이터베이스에 반영하는 기능을 제공합니다. 이 기능을 변경 감지(Dirty Checking)라고 합니다. 변경 감지는 트랜잭션이 커밋될 때 이루어지며, flush 메서드를 통해 처리됩니다.
변경 감지의 동작 원리
- 스냅샷 생성:
- 엔티티가 처음 영속성 컨텍스트에 저장될 때, JPA는 해당 엔티티의 스냅샷을 생성합니다. 이 스냅샷은 엔티티의 원래 상태를 기록한 것입니다.
- 트랜잭션 동안의 변경 사항:
- 트랜잭션 동안 엔티티의 상태가 변경되면, JPA는 영속성 컨텍스트 내의 1차 캐시에 변경된 엔티티를 저장합니다.
- 플러시(Flush):
- 트랜잭션이 커밋될 때 flush 메서드가 호출됩니다. 이 메서드는 영속성 컨텍스트 내의 모든 엔티티를 데이터베이스와 동기화합니다.
- flush는 명시적으로 호출할 수도 있지만, 보통 트랜잭션 커밋 시 자동으로 호출됩니다.
- 변경 감지:
- flush 메서드는 1차 캐시에 저장된 엔티티와 스냅샷을 비교하여 변경 사항을 감지합니다.
- 변경이 감지되면, JPA는 적절한 UPDATE SQL 문을 생성하여 데이터베이스에 변경 사항을 반영합니다.
아래의 코드를 보시면 이름을 변경한 후에 persist를 호출하지 않고 commit을 호출한 코드를 확인할 수 있습니다. 이렇게만 해주면 간편하게 update문이 생성되서 db에 반영되게 됩니다.
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Id;
import javax.persistence.Persistence;
@Entity
public class Member {
@Id
private Long id;
private String name;
// Getters and setters...
}
public class JpaExample {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("myPersistenceUnit");
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
// 엔티티 조회 - DB에서 데이터를 불러와 1차 캐시에 저장
Member member = em.find(Member.class, 1L);
// 엔티티 변경
member.setName("John");
// 트랜잭션 커밋 (flush 호출 포함)
em.getTransaction().commit(); // 이 시점에 flush가 호출되고, 변경 감지가 수행됨
em.close();
emf.close();
}
}
쓰기 지연
앞서 우리는 변경 감지(Dirty Checking)와 persist를 통해 데이터를 영속 상태로 만드는 것을 알아보았습니다. 그러나 객체를 생성할 때마다 데이터베이스에 쿼리를 전송하고, 변경을 감지할 때마다 쿼리를 전송하면 성능이 저하될 수 있습니다. 이를 해결하기 위해 JPA에서는 쓰기 지연(Write-behind) 메커니즘을 사용합니다.
쓰기 지연의 동작 원리
- 엔티티 영속화 및 변경 감지:
- 엔티티를 persist 메서드를 사용하여 영속화하거나 엔티티의 상태를 변경하면, JPA는 즉시 데이터베이스에 쿼리를 전송하지 않습니다.
- 대신, 해당 변경 사항을 영속성 컨텍스트의 1차 캐시에 저장하고, 변경된 엔티티의 상태를 기록합니다.
- 쓰기 지연 SQL 저장소:
- JPA는 트랜잭션이 커밋되기 전까지 변경된 엔티티와 관련된 SQL 문을 내부적으로 모아둡니다. 이 저장소는 보통 쓰기 지연 SQL 저장소라고 합니다.
- 트랜잭션 커밋 시점:
- 트랜잭션이 커밋되는 시점에 JPA는 영속성 컨텍스트의 모든 변경 사항을 플러시(Flush)합니다. 플러시는 변경된 엔티티의 상태를 데이터베이스에 반영하기 위해 SQL 문을 생성하고 실행하는 작업입니다.
- 즉, 트랜잭션 커밋 시점에 JPA는 모든 SQL 문을 한꺼번에 데이터베이스에 전송하여 성능을 최적화합니다.
Flush
Flush는 JPA의 영속성 컨텍스트에 있는 변경 내용을 데이터베이스에 동기화하는 작업을 의미합니다. Flush 작업은 영속성 컨텍스트를 비우지 않고, 엔티티의 상태 변경 사항을 데이터베이스에 반영합니다. 트랜잭션 내에서 Flush가 호출되면, 트랜잭션이 커밋되기 전에 변경된 모든 엔티티가 데이터베이스에 반영됩니다.
Flush 동작 원리
- Flush 시점:
- Flush가 호출되면 영속성 컨텍스트에 있는 모든 변경 사항이 데이터베이스에 동기화됩니다.
- JPA는 자동으로 트랜잭션 커밋 시 Flush를 호출합니다. 따라서 개발자는 명시적으로 Flush를 호출할 필요가 없지만, 특정 시점에 데이터베이스와 동기화가 필요할 때 수동으로 호출할 수 있습니다.
- Flush의 목적:
- 변경 내용을 즉시 데이터베이스에 반영하여 데이터의 일관성을 유지합니다.
- 쿼리 실행 전에 변경 사항을 반영하여 정확한 결과를 얻을 수 있게 합니다.
'개발 > Java' 카테고리의 다른 글
AOP의 필요성 및 개념 (0) | 2024.07.16 |
---|---|
Bean 의존관계 주입 방법 - 생성자 주입을 사용하자 (0) | 2024.07.14 |
스프링의 IoC(Inversion of Control)과 DI(Dependency Injection) (0) | 2024.07.10 |
디자인 패턴 - 팩토리 패턴 (0) | 2024.07.08 |
디자인패턴 - 어댑터 패턴 (0) | 2024.07.08 |