스프링을 배우다 보면, 영속성(Persist)이라는 말을 자주 사용한다. 대표적으로 영속성 컨텍스트(Persistence Context)라는 것으로 사용을 하는데, 누구든 알아들을 수 있도록
의 순서로 진행하고자 한다.
위키피디아에서는 영속성을 이렇게 정의한다.
영속성(persistence)은 데이터를 생성한 프로그램의 실행이 종료되더라도 사라지지 않는 데이터의 특성을 의미한다. 영속성은 파일 시스템, 관계형 테이터베이스 혹은 객체 데이터베이스 등을 활용하여 구현한다. 영속성을 갖지 않는 데이터는 단지 메모리에서만 존재하기 때문에 프로그램을 종료하면 모두 잃어버리게 된다. 결국 영속성은 특정 데이터 구조를 이전 상태로 복원할 수 있게 해주어 프로그램의 종료와 재개를 자유롭게 해준다.
정리하자면 어떠한 데이터의 상태를 유지시켜 주는 것이라고 할 수 있다.
대표적으로 영속성 컨텍스트에서 사용하는데, 그림으로 나타내면 아래와 같다.
DB를 공부했지만, Entity라는 용어는 그냥 스쳐가듯 지나간 경우가 많아서, 공부하기 전까지는 Entity를 'Spring 에서만 쓰는 것' 정도로 넘어가곤 했다.
일단 엔티티의 개념을 먼저 말하고 간다면, 엔티티는 '개체화되기 이전의 개념적 개체'를 뜻한다.
엔티티에서 정의된 요건들로 개체들을 만들어 테이블에 저장한다면, 실제적인 테이블이 되지만 그 이전의 개념적인 조건들을 정의한 상태를 엔티티라고 볼 수 있다.
즉 논리모델 = Entity, 물리모델 = Table 이다.
DB를 배우면서 공부했던 개념 단계를 생각해보면 된다.
위에서 영속성의 개념을 파악하고 내려왔지만, 영속성 컨텍스트를 정의하자면 엔티티를 영구 저장하는 환경 이라는 뜻이다.
애플리케이션과 데이터베이스 사이에서 객체를 보관하는 가상의 데이터베이스 같은 역할을 한다.
Spring에서는, EntityManager에 엔티티를 저장하거나 조회하면 EntityManager는 영속성 컨텍스트에 엔티티를 보관하고 관리한다.
em.persist(entity);
위와 같은 코드는 실제로 DB에 저장한다는 것이 아니라 영속성 컨텍스트를 통해서 Entity를 영속화시키겠다는 의미이다.
EntityManager가 생성되면 1:1로 영속성 컨텍스트가 생성된다.
하지만 컨테이너 환경의 JPA에서는 여러 EntityManager가 하나의 영속성 컨텍스트를 공유하게 된다.
컨테이너 환경의 JPA라 함은 무엇일까?
컨테이너를 사용하는 환경 (ex. spring)에서는 개발자가 EntityManager를 직접 생성하지 않고 컨테이너에 위임한다.
일반적으로 스프링은 싱글톤 기반으로 동작하기 때문에 속성값은 모든 thread가 공유하게 된다.
그래서 여러 thread가 동시에 접근하면 동시성 문제가 발생할 수도 있다.
그렇다면 스프링이 관리하는 EntityManager의 Thread-safe를 어떻게 보장할까?
EntityManager를 Proxy를 통해서 감싸고 EntityManager 메소드 호출 때 마다 Proxy를 통해서 EntityManager를 생성한다.
EntityManager를 직접 사용하는 경우에는 @PersistenceContext를 사용하면 된다.
(출처 : https://jaeho214.tistory.com/73 )
스프링 컨텍스트는 트랜잭션 범위의 영속성 컨텍스트 전략을 기본으로 사용한다.
트랜잭션이 시작될 때 영속성 컨텍스트를 생성하고 트랜잭션이 끝날 때 영속성 컨텍스트를 끝낸다.
즉, 트랜잭션이 같으면 같은 영속성 컨텍스트를 사용하는 것이다.
Entity의 생명 주기는 크게 비영속, 영속, 준영속, 삭제 네 가지로 나뉜다.
비영속 (new / transient)
Member member = new Member();
영속 (managed)
em.persist(member);
준영속 (detached)
// 엔티티를 영속성 컨텍스트에서 분리해 준영속 상태로 만든다.
em.detach(member);
// 영속성 컨텍스트를 비워도 관리되던 엔티티는 준영속 상태가 된다.
em.clear();
// 영속성 컨텍스트를 종료해도 관리되던 엔티티는 준영속 상태가 된다.
em.close();
삭제 (removed)
em.remove(member);
영속성 컨텍스트의 식별자 값
영속성 컨텍스트와 데이터베이스 저장
1차 캐시
// em.find(엔티티 클래스 타입, 식별자 값);
Member member = em.find(Member.class, "member1");
조회의 흐름
영속 엔티티의 동일성 보장
Member a = em.find(Member.class, "member1");
Member b = em.find(Member.class, "member1");
System.out.print(a==b) // true
트랜잭션을 지원하는 쓰기 지연(transactional write-behind)
변경 감지
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin();
Member member = em.find(Member.class, "member1");
member.setName("최권민");
transaction.commit();
참고한 모든 자료는 출처에 추가하고 있습니다. 만약 추가되지 않은 정보가 있다면, 말씀해 주시면 추가하도록 하겠습니다.