JPA 영속성

과녁스·2022년 2월 17일
0

Spring

목록 보기
8/14
post-thumbnail
post-custom-banner

개요


JPA를 학습하면서 가장 중요한 영속성 개념에 대하여 학습하면서 정리해보았습니다.

JPA 영속성


영속성 컨텍스트

엔티티를 영구 저장하는 환경이라는 뜻을 가지고 있다.

애플리케이션과 데이터베이스 사이에서 객체를 보관하는 역할을 한다. 엔티티 매니저를 통해 엔티티를 저장하거나 조회하면 엔티티 매니저는 영속성 컨텍스트에 엔티티를 보관하고 관리한다.

em.persist(car);

엔티티 매니저를 사용해 회원 엔티티를 영속성 컨텍스트에 저장한다는 의미!

영속성 컨텍스트의 특징

  1. 엔티티 매니저를 생성할 때 하나 만들어진다.
  2. 엔티티 매니저를 통해서 영속성 컨텍스트에 접근하고 관리할 수 있다.

Entity 생명주기

entity 생명주기

이미지 출처 : https://blog.woniper.net/266

  1. 비영속(new/transient): 영속성 컨텍스트와 전혀 관계가 없는 상태
  2. 영속(managed): 영속성 컨텍스트에 저장된 상태
  3. 준영속(detached): 영속성 컨텍스트에 저장되었다가 분리된 상태
  4. 삭제(removed): 삭제된 상태

비영속(new/transient)

entity 객체를 생성했지만, 아직 영속성 컨텍스트에 저장하지 않은 상태

Car car = new Car();

영속(managed)

entity manager를 통해서 entity 영속성 컨텍스트에 저장한 상태를 말하며, 영속성 컨텍스트에 의해 관리된다는 의미

EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("hello");
EntityManager entityManager = entityManagerFactory.createEntityManager();

entityManager.persist(car);

준영속(detached)

영속성 컨텍스트가 관리하던 영속 상태의 entitiy를 더이상 관리하지 않으면 준영속 상태로 변경된다. 특정 entity를 준영속 상태로 만드려면 em.detach() 를 호출

// 영속성 컨텍스트에서 분리해 준영속 상태로 변경
entityManager.detach(car);
// 영속성 컨텍스트를 비워도 관리되던 엔티티는 준영속 상태
entityManager.claer();
// 영속성 컨텍스트를 종료해도 관리되던 엔티티는 준영속 상태
entityManager.close();

준영속 상태에서는 아래와 같은 특징이 있다.

  • 1차 캐시, 쓰기 지연, 변경 감지, 지연 로딩을 포함한 영속성 컨텍스트가 제공하는 어떠한 기능도 동작하지 않는다.
  • 식별자 값을 가지고 있다.

삭제(removed)

entity를 영속성 컨텍스트와 데이터베이스에서 삭제

entityManager.remove(car);

영속성 컨텍스트의 특징

영속성 컨텍스트의 식별자 값

영속성 컨텍스트는 entity를 식별자 값으로 구분한다. 영속 상태에서는 반드시 식별자 값이 있어야 한다.

영속성 컨텍스트와 데이터베이스 저장

JPA는 보통 트랜잭션을 커밋하는 순간 영속성 컨텍스트에 새로 저장된 entitiy를 데이터베이스에 반영하는데 이것을 flush라고 한다.

영속성 컨텍스트가 Entity 관리 시 장점

  1. 1차 캐시
  2. 영속 entitiy의 동일성 보장
  3. 트랜잭션을 지원하는 쓰기 지연
  4. 변경 감지
  5. 지연 로딩

1차 캐시

영속성 컨텍스트 내부에는 캐시가 있는데 이를 '1차 캐시'라고 한다.
영속 상태의 entity를 이곳에 저장하고, 1차 캐시의 키는 식별자 값(기본키)이고, 값은 entity 인스턴스이다.

Car car = entityManager.find(Car.class, "car1");

조회 순서는 아래와 같다.

  1. 1차 캐시에서 entity를 조회
  2. 있다면 메모리에 있는 1차 캐시에서 entity 조회
  3. 없다면 데이터베이스에서 조회
  4. 조회한 데이터로 entity를 생성해 1차 캐시에 저장
  5. 조회한 entity를 반환

영속 엔티티의 동일성 보장

영속성 컨텍스트는 entity의 동일성을 보장한다

Car a = entityManager.find(Car.class, "car1");
Car b = entityManager.find(Car.class, "car1");

System.out.print(a==b); //true

트랜잭션을 지원하는 쓰기 지연

em.find(entity)를 사용하여 저장해도 바로 insert가 실행되는 것이 아닌 entity 매니저는 트랜잭션을 커밋하기 직전까지 내부 쿼리 저장소에서 insert 쿼리를 가지고 있는다. 그리고 트랜잭션을 커밋할 때 모아둔 쿼리를 DB에 보낸다. 이것을 트랜잭션을 지원하는 쓰기 지연이라고 한다.

변경 감지

entity 수정할 때는 조회 후 데이터를 변경하면 된다.

  1. 트랜잭션을 커밋하면 entity 매니저 내부에서 먼저 flush 호출
  2. entity와 스냅샷을 비교하여 변경된 entity 조회
  3. 변경된 entity가 있으면 수정 쿼리를 생성하여 쓰기 지연 SQL 저장소에 저장
  4. 쓰기 지연 저장소의 SQL을 flush
  5. 데이터베이스 트랜잭션을 커밋

변경 감지는 영속성 컨텍스트가 관리하는 영속 상태의 entity에만 적용

EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin();

Member member = em.find(Member.class, "member1");
member.setName("멤버1");

transaction.commit();

플러시

영속성 컨텍스트의 변경 내용을 데이터베이스에 반영하는 것을 flush 라고 한다.
영속성 컨텍스트의 entity를 지우는게 아니라 변경 내용을 데이터베이스와 동기화 하는 것이다.

  1. 변경 감지가 동작해서 스냅샷과 비교해서 수정된 entity 조회
  2. 수정된 entity에 대해서 UPDATE 쿼리를 만들어서 SQL 저장소에 저장
  3. 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송

@Transactional


@Transactional 어노테이션을 사용하여 Entitiy 생명주기를 코드로 확인해보았습니다.

// Service
// ...
class UserService {
	private final EntityManager entityManager;
	
    @Transactional
    public void put() {
    	// 비영속상태(new/transient)
    	User user = new User();
        user.setName("name");
        user.setEmail("name@test.com");
        
        // 영속상태(managed)
        entitiyManaver.persist(user);
        
        // 준영속(detached)
        entityManager.detach(user);
        
        // 영속상태에서 entity 변경이 감지되는 경우 Transaction이 끝나면 db update
        // dirty check
        // 대량의 데이터의 경우 성능 저하 우려
        
        // 준영속 상태에서는 db에 반영되지 않음
        // 반영을 하려면 entitiyManager.merge(user); 필요
        user.setName("newUserAfterPersist");
        
        // 모든 데이터가 drop
       	// 준영속상태에서 반영하려면 entityManager.flush() 필요
        entityManager.clear();
        
        // 삭제(remove) 상태
        entityManager.remove(user1);
        user1.setName("ttttt");	
        entitiyManager.merge(user1); // 이미 entityManager에서 삭제된 entity는 merge 불가 / 에러 발생
    }
    
    
}

출처


profile
ㅎㅅㅎ
post-custom-banner

0개의 댓글