티스토리 뷰

BackEnd/JPA 스터디

[JPA 스터디 1주차] - 영속성 관리

영지는 달리는중 2021. 6. 26. 20:20

JPA에서 가장 중요한 2가지

  • 객체와 관계형 데이터베이스 매핑하기
  • 영속성 컨텍스트

영속성 컨텍스트란

정의

  • '엔티티를 영구 저장하는 환경'
  • EntityManager로 접근이 가능하다 - em.persist(Entity);
  • 실제로 DB에 접근하기전에 영속성 컨텍스트 환경에 엔티티를 영속화 하겠다는 의미

EntityManager와의 관계 

  • 기본적으로 하나의 엔티티 매니저가 생성되면 1:1로 하나의 영속성 컨텍스트가 생성된다
  • 하지만 스프링 프레임워크와 같은 컨테이너 환경의 JPA에서는 여러 엔티티 매니저가 하나의 영속성 컨텍스트를 공유하게 된다

컨테이너 환경에서의 JPA

컨테이너를 사용하는 스프링과 같은 환경에서는 개발자가 EntityManager를 생성하지 않고 컨테이너가 생성 관리하게된다

 

스프링은 싱글톤 기반으로 동작하기 때문에 속성값들은 모든 스레드가 공유하게 되는데 이때 발생하는 EntityManager의 thread-safe를 어떻게 보장하는 것인가?

 

  • EntityManager를 Proxy를 통해 감싸고 EntityManger를 사용할 때 Proxy를 통해 EntityManager를 생성한다
  • EntityManager를 직접 사용하는 경우에는 @PersistenceContext를 사용하면 된다 
@PersistenceUnit
private EntityManagerFactory emf;
 
@PersistenceContext
private EntityManager em;
 
@Test
public void 엔티티매니저_호출시마다_새로운인스턴스리턴() {
    EntityManager em1 = emf.createEntityManager();
    EntityManager em2 = emf.createEntityManager();
    EntityManager em3 = emf.createEntityManager();
 
    System.out.println(em1);
    System.out.println(em2);
    System.out.println(em3);
    System.out.println(em);
 
    assertThat(em1, is(not(sameInstance(em2))));
    assertThat(em1, is(not(sameInstance(em3))));
    assertThat(em, is(not(sameInstance(em1))));
    assertThat(em, is(not(sameInstance(em2))));
    assertThat(em, is(not(sameInstance(em3))));
}
org.hibernate.jpa.internal.EntityManagerImpl@739831a4
org.hibernate.jpa.internal.EntityManagerImpl@7e3236d
org.hibernate.jpa.internal.EntityManagerImpl@38a4e2b0
Shared EntityManager proxy for target factory [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@14c141c0]
cs
  • @PersistenceContext로 주입받은 엔티티 매니저는 SharedEntityManageCreator에 의해 Proxy로 만들어지고
  • EntityManagerFactory를 통해 엔티티 매니저를 생성한 객체들은 Proxy로 감싸져 있지 않는것을 볼 수 있다 

영속성 컨텍스트의 이점

 

 

1. 1차 캐시

영속성 컨텍스트 내부에는 Map<Key,Value> 형태로 저장되는 1차 캐시가 존재한다 (Key = id, Value = member entity)

1차 캐시로 반복 가능한 읽기 등급의 트랜잭션 수준을 디비가 아닌 애플리케이션 차원에서 제공한다. 트랜잭션이 종료되는 시점에 캐시도 모두 날라가기 때문에 애플리케이션이 작을 때는 큰 효과를 볼 수 없지만 비즈니스 로직이 복잡한 경우 성능 향상의 효과를 볼 수 있다. 

Member member = new Member();
member.setId("memberID");
member.setPw("password");
// 트랜잭션 시작 - 영속성 컨텍스트 생성
tx.begin();
// 영속성 컨텍스트에 저장
em.persist(member); 
// 1차 캐시에서 조회
Member findMember = em.find(Member.class, "memberID"); 

2. 동일성 보장(identity)

Member member1 = entityManager.find(Member.class, "member1");
Member member2 = entityManager.find(Member.class, "member1");
System.out.println(member1 == member2); // 동일성 비교 true

 

3. 트랜잭션을 지원하는 쓰기 지연 (버퍼)

member1을 캐시에 저장 후 쓰기 지연 SQL 저장소에 insert 쿼리를 저장한다

member2도 동일하게 캐시에 저장후 insert 쿼리를 저장한다

commit을 하는 시점에 저장된 insert 쿼리문을 모두 DB에 보냄으로써(=flush()) 버퍼의 기능을 제공한다

public void createMember() {
    EntityManager em = emf.createEntityManager();
    EntityTransaction tx = em.getTransaction();
    
    tx.begin();
    
    Member member1 = new Member("Id1", "pw1");
    Member member2 = new Member("Id2", "pw2");
    
    //두개의 Insert 쿼리문을 영속성 컨텍스트의 쓰기 지연 SQL 저장소에 쌓아둠
    em.persist(member1);
    em.persist(member2);
    
    //commit 하는 순간 DB에 쌓아둔 쿼리를 모두 보냄
    tx.commit();
}

 

4. 더티 체킹(Dirty Checking)

Entity는 영속성 컨텍스트에 저장될 때 스냅샷을 생성한다. 

수정하려는 객체를 조회하면 영속상태가되고 1차 캐시에 관리됨과 동시에 스냅샷이 생성되고 commit 시점에 스냅샷과 비교하여 객체가 수정되었다면  별도의 update() 명령이 필요없이 수정된 데이터를 반영하여 flush()하여 commit을 하게된다. 

EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();

tx.begin();

// 영속 엔티티 조회 - 1차 캐시 저장 & 스냅샷 생성 
Member memberA = em.find(Member.class, "memberA");

// 영속 엔티티 데이터 수정
memberA.setUsername("hi");
memberA.setAge(10);

//커밋 전 영속성 컨텍스트에서 스냅샷과 비교하여 데이터 변경 감지 후 커밋 시 DB에 적용
tx.commit();

 

Entity 생명주기(Entity LifeCycle)

  • 비영속 (new / transient) : 객체를 생성한 상태, 영속성 컨텍스트와 전혀 관계가 없는 상태
Member member = new Member();
member.setId("memberID");
member.setPW("password");

 

  • 영속 (managed) : 트랜잭션을 시작하여 객체를 영속성 컨텍스트에 저장, 관리되고 있는 상태
Member member = new Member();
member.setId("memberID");
member.setPw("password");

EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();

tx.begin();

em.persist(member);

 

  • 준영속 (deteached) : 영속성 컨텍스트에 있다가 영속성 컨텍스트에서 지운 상태
detech(); // 더이상 영속성 컨텍스트에서 관리하지 않겠다고 선언

em.detach(entity) : 특정 엔티티만 준영속 상태로 전환

em.clear() : 영속성 컨텍스트를 완전히 초기화 

e,.close() : 영속성 컨텍스트를 종료

 

  • 삭제(removed) : 실제 DB 삭제를 요청한 상태 
em.remove(member);

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/10   »
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 31
글 보관함