✅ 영속성 컨텍스트
♐ 영속성 컨텍스트란?
- Entity 객체를 영속성 상태로 관리하는 일종의 캐시 역할을 하는 공간으로 여기에 저장된 Entity는 데이터베이스와 자동으로 동기화되며 같은 트랜잭션 내에서는 동일한 객체가 유지된다.
♐ 작동 원리
- 눈에 보이지 않는 공간이 생긴다(논리적 개념)
- Entity Manager 를 통해서 영속성 컨텍스트에 접근한다. ex) EntityManager.persist(entity)
- Entity를 영속성 컨텍스트에 저장한다.
✅ Entity
♐ Entity란?
- 데이터베이스에서 Entity란 저장할 수 있는 데이터의 집합을 의미한다.
- JPA 에선 테이블을 나타내는 클래스이다.
♐ 생명 주기
1. 비영속 (New/Transient):
- 영속성 컨텍스트와 전혀 관계가 없는 상태
- 객체가 처음 생성된 상태로, 데이터베이스와 연결되지 않다.
- 이 상태에서 persist() 메서드를 호출하면 객체가 관리되는 상태로 전환된다.
// 객체를 생성하고 영속성 컨텍스트에 저장하지 않은 상태
User user = new Tutor(1L, "user1", 100);
2. 영속 (Managed):
- 객체가 데이터베이스와 연결된 상태로, 변경사항이 자동으로 데이터베이스에 반영된다.
- 예를 들어, 사용자의 정보를 수정하면 그 변경이 즉시 데이터베이스에 저장된다.
- remove() 메서드를 호출하면 객체가 삭제 상태로 전환된다. 이때 객체는 여전히 메모리에 존재하지만, 데이터베이스에 서 삭제되도록 예약된다.
// EntityManagerFactory 생성
EntityManagerFactory emf = Persistence.createEntityManagerFactory("test");
// EntityManager 생성
EntityManager em = emf.createEntityManager();
// Transaction 생성
EntityTransaction transaction = em.getTransaction();
// 트랜잭션 시작
transaction.begin();
em.persist(user);
- persist 후 출력 이후에 SQL이 실행된다.
3. 삭제 (Removed):
- 객체가 영속 상태에서 삭제된 상태로, 데이터베이스에서 실제로 삭제되기 전이다.
- 이 상태에서 flush() 메서드를 호출하면 데이터베이스에서 객체가 완전히 삭제된다.
// 객체를 삭제한 상태
em.remove(user);
4. 준영속 (Detached):
- 객체가 영속 상태에서 분리된 상태로, 데이터베이스와의 연결이 끊어진 상태이다.
- 세션이 종료되거나 명시적으로 detach() 메서드를 호출하여 객체가 관리되지 않게 된다.
- 영속성 컨텍스트가 제공하는 기능을 사용하지 못한다.
- 이 객체는 여전히 메모리에 존재하지만, 데이터베이스의 변경사항과 동기화되지 않는다. merge() 메서드를 통해 다시 관리 상태로 전환할 수 있다.
- 준영속 상태로 만드는 방법
1. em.detach()
- 특정 Entity만 준영속 상태로 변경한다.
2. em.clear()
- 영속성 컨텍스트를 초기화 한다.
3. em.close()
- 영속성 컨텍스트를 종료한다.
5. DB:
- 데이터베이스 자체를 나타낸다. find() 메서드를 통해 데이터베이스에서 객체를 검색할 수 있고, flush() 메서드를 통해 변경사항을 반영한다.
✅ 1차 캐시
♐ 1차 캐시란?
- 엔티티를 영속성 컨텍스트에 저장할 때 생성되는 메모리 내 캐시이다. 엔티티는 먼저 1차 캐시에 저장되고 이후 같은 엔티티를 요청하면 DB를 조회하지 않고 1차 캐시에서 데이터를 반환하여 성능을 높일 수 있다.
- 1차 캐시 코드예시
public static void main(String[] args) {
// EntityManagerFactory 생성
EntityManagerFactory emf = Persistence.createEntityManagerFactory("test");
// EntityManager 생성
EntityManager em = emf.createEntityManager();
// Transaction 생성
EntityTransaction transaction = em.getTransaction();
// 트랜잭션 시작
transaction.begin();
try {
// 비영속
Tutor tutor = new Tutor(1L, "wonuk", 100);
// 영속
System.out.println("persist 전");
em.persist(tutor);
System.out.println("persist 후");
Tutor findTutor = em.find(Tutor.class, 1L);
System.out.println("findTutor.getId() = " + findTutor.getId());
System.out.println("findTutor.getName() = " + findTutor.getName());
System.out.println("findTutor.getAge() = " + findTutor.getAge());
// transaction이 commit되며 실제 SQL이 실행된다.
transaction.commit();
} catch (Exception e) {
// 실패 -> 롤백
e.printStackTrace();
transaction.rollback();
} finally {
// 엔티티 매니저 연결 종료
em.close();
}
emf.close();
}
- 조회 SQL이 실행되지 않는다
✅ 동일성 보장
- 동일한 트랜잭션 안에서 특정 엔티티를 여러 번 조회해도 항상 같은 객체 인스턴스를 반환한다.
- 영속성 컨텍스트는 1차 캐시를 사용하여 같은 엔티티를 중복 조회해도 동일한 객체를 참조하게 하여 일관성을 유지한다.
- 동일한 트랜잭션 내에서 조회된 Entity는 같은 인스턴스를 반환한다.
- DB에 저장된 데이터를 조회하여 1차 캐시에 저장한다.
- 1차 캐시에 저장된 데이터를 조회한다.
✅ 쓰기 지연
- 엔티티 객체의 변경 사항을 DB에 바로 반영하지 않고 트랜잭션이 커밋될 때 한 번에 반영하는 방식으로 이를 통해 성능을 최적화하고 트랜잭션 내에서의 불필요한 DB 쓰기 작업을 최소화한다.
♐ 그림으로 이해하기
1. `tutor1` 1차 캐시에 저장
2. 쓰기 지연 저장소에 `tutor1 INSERT SQL` 저장
3. `tutor2` 1차 캐시에 저장
4. 쓰기 지연 저장소에 `tutor2 INSERT SQL` 저장
1. `flush` 되면서 SQL 쿼리 실행
2. 트랜잭션이 `Commit` 되면서 실제 DB에 반영
✅ 변경 감지
- 영속성 컨텍스트가 엔티티의 초기 상태를 저장하고 트랜잭션 커밋 시점에 현재 상태와 비교해 변경 사항이 있는지 확인하는 기능이다.
♐ 그림으로 이해하기
1. Entity 가 1차캐시에 저장되면 Snapshot 에도 저장한다.
2.다음 트렌젝션이 들어와 Snapshot 과 값을 비교하여 다르면 자동으로 UPDATE SQL을 생성한다.
'Spring' 카테고리의 다른 글
[SPRING/JPA]양방향 관계시 사용하는 CASCADE에 대해 알아보자 (1) | 2025.04.11 |
---|---|
[Spring] Annotation (2) | 2025.04.10 |
[Spring] Controller 와 RestContoller 의 차이 (0) | 2025.04.10 |
[Spring] MVC 패턴 (0) | 2025.04.07 |
[Spring]HTTP 란? (2) (0) | 2025.03.31 |