영속성 컨텍스트(Persistence Context)는 객체와 데이터베이스 사이의 매핑을 관리하는 엔티티 매니저(Entity Manager)의 일종의 캐시 영역
이는 자바 ORM(Object-Relational Mapping) 기술인 JPA(Java Persistence API)에서 핵심적인 역할을 수행합니다.
영속성 컨텍스트는 엔티티의 생명주기를 관리하며, 데이터베이스와의 동기화를 통해 데이터 일관성을 유지합니다.
1.1 Entity와 Entity 관리
Entity
데이터베이스의 테이블과 매핑되는 객체(자바 클래스)
@ Entity 사용
Entity Manger
엔터티의 생명주기와 밀접한 관련성
⇒ 프로젝트의 복잡성이
Entity의 생명주기
영속성 컨텍스트의 특징/ 이점
- 1차 캐시 (First Level Cache):
- 영속성 컨텍스트는 엔티티를 메모리에 캐시하여 동일한 엔티티를 반복적으로 데이터베이스에서 조회하지 않도록 합니다. 이 캐시는 같은 트랜잭션 내에서만 유효합니다.
- 엔티티 동일성 보장 (Entity Identity):
- 동일한 영속성 컨텍스트 내에서는 동일한 데이터베이스 레코드를 조회할 때마다 동일한 엔티티 인스턴스가 반환됩니다.
- 변경 감지 (Dirty Checking):
- 영속성 컨텍스트는 트랜잭션이 종료될 때 엔티티의 변경 사항을 자동으로 감지하고, 필요한 경우 데이터베이스에 반영합니다.
- 쓰기 지연 (Write Behind):
- 트랜잭션이 커밋되기 전까지 변경된 데이터를 데이터베이스에 즉시 반영하지 않고, 커밋 시점에 일괄적으로 반영하여 성능을 최적화합니다.
- 트랜잭션 범위:
- 영속성 컨텍스트는 일반적으로 트랜잭션 범위와 일치합니다. 트랜잭션이 시작되면 새로운 영속성 컨텍스트가 생성되고, 트랜잭션이 종료되면 해당 컨텍스트가 닫힙니다.
영속성 컨텍스트의 과정
- 엔티티 매니저 생성:
- 애플리케이션이 시작되면 EntityManagerFactory를 통해 EntityManager를 생성합니다.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("example-unit");
EntityManager em = emf.createEntityManager();
- 트랜잭션 시작:
- 엔티티 매니저를 통해 트랜잭션을 시작합니다.
em.getTransaction().begin();
- 엔티티 조회:
- find 또는 query 메소드를 사용하여 엔티티를 조회합니다. 조회된 엔티티는 영속성 컨텍스트에 캐시됩니다.
MyEntity entity = em.find(MyEntity.class, entityId)
- 엔티티 변경:
- 엔티티의 속성을 변경하면 영속성 컨텍스트가 이를 감지합니다.
entity.setName("New Name");
- 엔티티 저장:
- persist 메소드를 사용하여 새로운 엔티티를 영속성 컨텍스트에 추가합니다.
em.persist(newEntity);
- 트랜잭션 커밋:
- 트랜잭션을 커밋하면, 영속성 컨텍스트는 변경된 엔티티를 데이터베이스에 반영합니다. 이 과정에서 변경 감지(dirty checking)를 통해 수정된 엔티티만 업데이트됩니다.
em.getTransaction().commit();
- 영속성 컨텍스트 종료:
- 트랜잭션이 종료되면 영속성 컨텍스트도 종료됩니다. EntityManager를 닫아야 합니다.
em.close();
수업 내용
영속성 컨텍스트 & 엔터티 관리
영속성 컨텍스트
- 컨텍스트는 컨테이너라는 의미
- 엔터티를 저장해서 관리하는 공간 또는 영역
- 컨테이너의 핵심 기능 : persistence.xml의 메타 정보를 참조하여 애플리케이션 운영에 필요한 객체를 생성하고 관리
- 영속성 컨테이너는 persistence.xml의 메타 정보를 참조하여 메타 정보를 로딩하고, Entity Manger 객체를 생성할 때 자동으로 생성
- Entity Manger가 제공하는 메소드를 통해서만 접근이 가능
- 영속성 컨테이너가 관리하는 엔터티의 상태는 생성, 관리, 분리, 삭제 의 네가지 상태로 구분
관리 상태
- 엔터티를 연속성 컨테이너에 등록
- Entity Manger의 persist() : 엔터티 생성 → 컨테이너에 등록
- Entity Manger의 find() :
- DB 조회 → 엔터티 생성 → 컨테이너에 등록 → 요청한 쪽에 엔터티를 전달
- find() 메소드를 사용한다고 해서 항상 DB에서 조회하는 것은 아니다
- 영속성 컨테이너에서 먼저 조회 후 DB에서 조회
- Entity 가 DB에 저장(insert)
- 반드시 transaction 안에서 persist() 호출이 되어야 함
분리 상태
- 영속 컨테이너에 있던 엔터티가 특정 작업에 의해서 영속 컨테이터를 벗어난상태
- 엔터티를 수정해도 데이터베이스에 반영이 되지 않음 ⇒ Dirty Checking 을 수행하지 않음
- 생성상태와 분리 상태의 차이점
- 생성 상태 : persist() 를 호출하지 않음 ⇒ 식별자가 없는 상태
- 분리 상태 : persist() 를 호출한 적이 있음 ⇒ 식별자가 있는 상태
- 분리 상태 전환을 위한 Entity Manger 의 메소드
- detach(entity) : 특정 Entity 만 분리
- clear() : 컨테이너 초기화 ⇒ 영속 컨테이너가 관리하는 모든 엔티티를 분리
- close() : 컨테이터 종료 ⇒ Manger 가 관리하는 entity 가 있다면 먼저 비영속 상태로 모두 전환 후, 종료
JPA 사용시 중요한 2가지 부분
- 설계 부분 : JPA 엔터티와 테이블 매핑
- 사용 부분 : 실제 사용 부분
영속성 컨텍스트
엔터티를 영구 저장하는 환경 ⇒ DB에 저장되는 것은 아니다
EntityManger를 통해 저장
- 장점
- 1차 캐시, 동일성 보장, transaction 지원, 쓰기 지원, 변경 감지
- 1차 캐시
- find()
- → 영속성 컨텍스트에서 조회(1차 캐시에서 조회 ⇒ DB에 요청하지 않아도 됨)
- → DB에서 조회(1차 캐시에 저장)
- find()
- 동일성 보장Employee emp2 = em.find(Employee.class, 1L);영속성 컨텍스트에서 식별자로 관리되는 엔터티는 동일
- emp1 == emp2 ? ⇒ true 반환
- Employee emp1 = em.find(Employee.class, 1L);
- transaction
- (JPA 에서의 transaction) 변경된 사항 ⇒ 쓰기 지연
⇒ 트랜잭션을 지원하는 쓰기 지연
트랜잭션을 커밋을 할 때 모아둔 모든 쿼리들을 한번에 데이터베이스에 전송
(쓰기 지연 SQL 저장소)
- 변경 감지 (Dirty Checking)
개발자에게는 update sql 작성을 하지 않아도 됨→ update sql 을 작성해서 쓰기 지연 SQL 저장소에 저장emp.setName(”이름변경”) transaction.commit()
- → 데이터베이스에 전송
- 스냅샷(원본)과 변경된 엔터티(1차 캐시에 저장된)를 비교
- 영속성 관리 상태의 엔터티
- flush()영속성 컨텍스의 변경 내용을 테이터베이스에 반영하는 작업을 수행작성된 SQL은 쓰기 지연 SQL 저장소에 저장이 되고 데이터베이스에 전송
- 영속성 컨텍스트에 있는 모든 엔터티를 스냅샷과 비교해서 수정이 발생된 엔터티를 찾고, 해당 수정에 맞는 SQL 이 작성된다
- 트랜잭션 커밋 시 flush() 호출
Entity 영속성 상태 관리
항상 데이터를 조회한다고 해서 DB에서 조회하는 것이 아니다
- transaction 바깥에서 entity를 변경할 경우 Entity Manger에는 반영이 되지만 DB에는 반영이 되지 않음
- ex) find() 메서드 사용 1
- transaction 바깥에서 employee.setName()으로 entity 값을 변경한다transcation.commit() → employee.setName() → em.find() → DB에는 반영이 되지 않음
- entity 변경된 값은 transaction에 반영되지 않고 Entity Manger 만 값 변경을 가지고 있다
- find() 메소드 사용 2 (새로운 thread)entity manger 가 조회할 수 있는 1차 캐시에는 데이터에이스 값이 생성 된 것이 없으므로 아무 데이터가 없다
- 그러므로 사용자의 find() 메서드 요구를 찾기 위해 entity manger는 DB에서 데이터를 가져와야 하고 select 문을 실행하여서 데이터를 가져와 영속성 컨테이너)에 저장하고 사용자의 find() 메서드를 실행한다
- 새로운 entity manger를 생성해서 기존에 있던 테이블의 값을 조회를 한다
- ex) find() 메서드 사용 1
- 준영속성 상태에서의 데이터 상태
- detach() 로 변경된 entity에게 데이터 수정을 하였을 때 DB에 반영이 되지 않는다
- 준영속성 상태에서 find() 메소드로 데이터를 조회를 하면 (영속성 컨텍스트)가 존재하지 않으므로 DB에서 select 문으로 들고와서 엔터티에 할당을 하고 다시 영속 상태인 entity를 entity Manger 가 관리를 한다 (즉, 영속성 컨테이너 생성)
- 삭제 상태에서의 데이터 상태
- 생성한 entity 를 entity Manger를 사용하여 remove()를 해주면 entity가 삭제가 된다
- 삭제라는 말은 단순히 entity 객체가 없어지는 것이 아니라 DB에서 entity 객체와 함께 할당이 된 table이 삭제가 되는 것이다
- 중요!!!!!! =⇒ 삭제는 DB 삭제도 같이 하는 것이다
- 생성한 entity 를 entity Manger를 사용하여 remove()를 해주면 entity가 삭제가 된다
- 준영속 상태에서 다시 영속 상태로의 변환
- detach()로 준영속 상태에 들어간 entity는 데이터라고 할 수 없고 단순힌 자바의 클래스 상태
- 이것을 다시 entity manger로 영속 상태로 만들어 주기 위해 merge() 메서드 사용
- 클래스로 전환된 이전 entity 인스턴스를 merge() 메서드에 넣어주고 entity로 전환된 인스턴스를 받아 이전 entity 에 할당을 해줌
- EntityManger em = new EntityManger(); Employee employee = new Employee(); // em.detach(employee); // 일반 인스턴스 객체로 변한 employee에 다시 영속 상태가 된 인스턴스를 할당 employee = em.merge(employee);
- 조심할 것!!!
- 만약 준영속으로 바뀐 후에 entity 클래스를 수정을 한 후에 다시 merge()로 영속 상태로 전환을 하면 merge()는 DB에 반영되어진 데이터베이스의 값을 가져오기 때문에 준영속 상태에서 수정한 값의 변화는 없어진다
- 영속 → 준영속 → 영속 으로 상태 변화createEntity() : 새로운 영속 컨텐스트를 생성하고 엔터티를 영속 상태로 만든뒤 준영속으로 변환
- 순서
- createEntity() 로 영속 → 준영속 엔터티 생성 후 준영속 엔터티 반환
- 준영속 엔터티의 데이터 값 변경 (setName())
- 준영속 엔터티를 mergeEntity()로 영속 엔터티로 변환한 새로운 인스턴스를 생성하여 같은 내용 두가지 버전의 엔터티가 생성됨
- 순서
- mergeEntity() : 준영속 상태인 엔터티를 관리하기 위해 또다른 영속 컨텍스트를 생성하고 준영속 상태 엔터티로 영속 상태 엔터티로 재생성하여 같은 내용 다른 버전의 두가지 엔터티가 생성되게 하는 함수
- 두개의 함수 존재
!!!! 중요
이때 엔터티는 값을 저장해놓고 있어서 이 값을 바로 보는 것이 아니라 테이블에 어떠한 값이 정의되어 있다고 명시해놓은 설명서 같은 것이다
또한 엔터티는 하나의 식별자가 가리키는 값들만 저장하고 있다
여러개를 저장하려고 하면 리스트로 엔터티들을 저장해야 한다
이 개념을 생각하고 이 부분을 읽을 것 !!!!!!!!!!!!!!!
- mergeEntity() 분석
- merge() 호출 : 준영속 상태의 변경이 발생한 엔터티를 매개변수로 전달
- 새롭게 생성된 영속성 컨테이너에는 매개변수로 받은 엔터티의 테이블이 없는 상태
- 1차 캐시는 매개변수로 받은 엔터티와 연결된 테이블의 값이 존재하지 않으므로 데이터베이스로 가서 엔터티가 정의한 테이블 값을 가져와야 함
- ⇒ select 문장이 작성되고, 전송이 됨
- DB 검색 결과를 바탕으로 테이블 값을 생성하고 1차 캐시에 저장
- 1차 캐시에 저장된 테이블 값을 매개변수로 받은 준영속 상태 엔터티에서 정의한 것과 비교하여 준영속 상태 엔터티에서 변경이 발생한 부분을 반영
- 새로운 영속 엔터티의 name 은 “홍길동” 이고,따라서, “홍길동”을 “이름 변경”으로 수정해야 함
- ⇒ update 문장이 작성되고, DB로 전송이 됨
- 준영속 상태의 엔터티 name의 값은 “이름변경 “ 인 상태
- 매개변수로 전달된 준영속 엔터티를 식별자 값으로 1차 캐시에서 엔터티 검색을 수행
- 모든 변경 사항이 반영이 되어 동기화가 이루어진 상태
- 최종적으로 수정사항이 모두 반영된 영속 엔터티 반환
- 현재 두가지 entity 가 공존하고 있고, entity 와 데이터의 영속 및 동기를 위해 merge() 가 반환하는 entity를 사용해야 함
'BackEnd' 카테고리의 다른 글
도커와 쿠버네티스 -Docker 2 (Config Server) (0) | 2024.10.15 |
---|---|
쿠버네티스 기초 (0) | 2024.10.01 |
[JPA] Framework (0) | 2024.08.26 |
[JPA] Spring과 JPA-3 (0) | 2024.07.31 |
[JPA] Spring과 JPA-2 (0) | 2024.07.30 |