JPA Persistence Context 개념과 특징

devyumi·2024년 10월 27일
0

Spring

목록 보기
14/14

Persistence Context


  • 영속성 컨텍스트란 엔티티를 관리하는 환경을 뜻한다.

  • 엔티티 객체를 데이터베이스와 연결하여 객체의 생명주기를 관리한다.


EntityManagerFactory


  • EntityManager을 생성하는 인터페이스이다.

  • 일반적인 웹 애플리케이션 시작 시 한 번 생성되며, 다수의 EntityManager 인스턴스가 이를 공유하며 사용한다.


EntityManager


  • 영속 상태의 엔티티를 관리하는 인터페이스이다.

  • HTTP 요청이나 트랜잭션이 시작될 때 EntityManagerFactory에 의해 생성되며, 요청을 완료하거나 트랜잭션이 종료되면 자원을 반납한다.

  • 멀티스레드 환경에서 EntityManager을 사용하는 것은 위험성을 동반하는데, 이는 엔티티가 공유될 경우 데이터 무결성 문제가 발생할 수 있기 때문이다.

    각 트랜잭션은 서로의 상태에 영향을 미치지 않아야 하며(isolation) 멀티스레드 환경에서는 독립적인 트랜잭션을 처리하기 어려워진다. 이를 해결하기 위해 @PersistenceContext 어노테이션을 사용할 수 있다. 이는 각 트랜잭션마다 새로운 EntityManager 인스턴스를 주입받도록 하여 트랜잭션의 독립성을 유지할 수 있도록 지원한다.



Persistence Context Life Cycle


1) 비영속 (Transient)

  • 엔티티가 영속성 컨텍스트와 관계가 없는 상태

2) 영속 (Persistent)

  • 엔티티가 영속성 컨텍스트에 저장되어 관리되고 있는 상태

  • persist(): 새로운 엔티티 객체를 영속성 컨텍스트에 추가할 수 있다.


3) 준영속 (Detached)

  • 엔티티가 영속성 컨텍스트에 저장되었다가 분리된 상태

  • merge(): 준영속 상태의 엔티티를 영속성 컨텍스트에 추가할 수 있다.

    id 식별자는 그대로 유지되지만 새로운 인스턴스가 반환되어 기존 객체와의 참조가 달라진다. 이는 영속성 컨텍스트의 장점인 동일성 보장에 어긋남과 동시에 객체의 모든 필드를 업데이트 하기 때문에 성능 저하를 불러온다.


  • 준영속 상태 만드는 법

    1) detached(): 특정 엔티티만 준영속 상태로 만든다.

    2) clear(): 영속성 컨텍스트를 완전히 초기화하고, 모든 엔티티를 준영속 상태로 만든다.

    3) close(): 영속성 컨텍스트를 종료하고 자원을 해제한다. 이후 EntityManager을 사용할 수 없다.


4) 삭제 (Removed)

  • 엔티티가 삭제된 상태

비교

DB 존재Id 할당
비영속XX
영속OO
준영속OO
삭제XX


Persistence Context 장점



1) 1차 캐시 & 동일성 보장


  • 엔티티가 영속 상태가 되면 1차 캐시에 저장된다.

  • 엔티티는 Map의 key-value(@Id, Entity) 형태로 저장되며 @Id에는 primary key, Entity에는 엔티티가 저장된다.

  • 1차 캐시에 엔티티 객체가 저장되어 있는 경우 DB에 접근하지 않고 1차 캐시를 통해서 데이터를 반환하여 DB 접근을 최소화할 수 있다. 또한, 식별자가 같을 경우 동일성을 보장하여 데이터 일관성을 유지할 수 있다는 이점을 가진다.


다음과 같은 코드를 실행했다고 가정하자.

Member member1 = new Member(1, "Jane", 25);
em.persist(member1);									//... 1)
>
Member member1 = em.find(Member.class, 1);				//... 2)
Member member2 = em.find(Member.class, 2);				//... 3)
>
System.out.println("is same?: " + (user1 == user2));	//... 4)

1) 1차 캐시에 Id 식별자가 1인 member1 엔티티가 저장된다.

2) Id가 1인 member1 엔티티를 조회한다. 해당 엔티티는 1차 캐시에 존재하기 때문에 1차 캐시를 통해서 데이터가 반환된다.

3) Id가 1인 member2 엔티티를 조회한다. member2 엔티티는 1차 캐시에 존재하지 않기 때문에 DB에서 조회 후 1차 캐시에 (1, member2)를 저장한다.

4) member1, member2가 같은 객체를 참조하는지 확인한다. 둘은 같은 Id 식별자를 가지기 때문에 동일한 객체이므로 true를 반환한다.


2) 쓰기 지연


  • EntityManger은 엔티티의 상태를 데이터베이스에 즉시 반영하지 않고, 트랜잭션이 커밋될 때까지 지연하는 전략을 사용한다. 영속성 컨텍스트 내부 쿼리 저장소에 Insert Query를 적재한 후 커밋 시 데이터베이스에 반영한다. 이는 DB의 I/O 작업을 줄여 성능을 향상해준다는 장점을 가진다.

다음 코드를 수행한다고 가정하면, em.persist()를 수행할 때마다 DB에 접근하는 것이 아니라, 트랜잭션 커밋 시 1번만 DB에 접근하는 것이다.

try {
	transaction.begin();
  	em.persist(new Member(1, "Jane", 25);
  	em.persist(new Member(2, "Tom", 12);
  	em.persist(new Member(3, "Jack", 39);
	em.persist(new Member(4, "May", 27);
>
    transaction.commit();
} ...

3) 변경 감지 (Dirty Checking)


  • 엔티티의 변경사항을 감지하여 데이터베이스에 엔티티를 자동으로 업데이트 하는 전략이다.

  • 트랜잭션을 커밋하면 스냅샷(엔티티 객체의 초기상태)과 persist한 객체의 상태를 비교하여 변경사항이 있는지 확인한다. persist한 객체와 스냅샷이 다를 경우 UPDATE QUERY를 생성하여 쓰기 지연 저장소에 적재한 뒤 데이터베이스에 반영한다.

try {
	Member member = new Member(1, "Jane" 25);

	transaction.begin();
    em.persist(member);		//member 스냅샷 생성됨
  
    member.setName("Jack");
    em.persist(member);		//member 스냅샷과 비교하여 변경사항이 발생하여 UPDATE QUERY 생성
  
    transaction.commit();
} ...

참고

0개의 댓글

관련 채용 정보