JPA를 학습하면서 가장 중요한 영속성 개념에 대하여 학습하면서 정리해보았습니다.
엔티티를 영구 저장하는 환경이라는 뜻을 가지고 있다.
애플리케이션과 데이터베이스 사이에서 객체를 보관하는 역할을 한다. 엔티티 매니저를 통해 엔티티를 저장하거나 조회하면 엔티티 매니저는 영속성 컨텍스트에 엔티티를 보관하고 관리한다.
em.persist(car);
엔티티 매니저를 사용해 회원 엔티티를 영속성 컨텍스트에 저장한다는 의미!
이미지 출처 : https://blog.woniper.net/266
entity 객체를 생성했지만, 아직 영속성 컨텍스트에 저장하지 않은 상태
Car car = new Car();
entity manager를 통해서 entity 영속성 컨텍스트에 저장한 상태를 말하며, 영속성 컨텍스트에 의해 관리된다는 의미
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("hello");
EntityManager entityManager = entityManagerFactory.createEntityManager();
entityManager.persist(car);
영속성 컨텍스트가 관리하던 영속 상태의 entitiy를 더이상 관리하지 않으면 준영속 상태로 변경된다. 특정 entity를 준영속 상태로 만드려면 em.detach() 를 호출
// 영속성 컨텍스트에서 분리해 준영속 상태로 변경
entityManager.detach(car);
// 영속성 컨텍스트를 비워도 관리되던 엔티티는 준영속 상태
entityManager.claer();
// 영속성 컨텍스트를 종료해도 관리되던 엔티티는 준영속 상태
entityManager.close();
준영속 상태에서는 아래와 같은 특징이 있다.
entity를 영속성 컨텍스트와 데이터베이스에서 삭제
entityManager.remove(car);
영속성 컨텍스트는 entity를 식별자 값으로 구분한다. 영속 상태에서는 반드시 식별자 값이 있어야 한다.
JPA는 보통 트랜잭션을 커밋하는 순간 영속성 컨텍스트에 새로 저장된 entitiy를 데이터베이스에 반영하는데 이것을 flush라고 한다.
영속성 컨텍스트 내부에는 캐시가 있는데 이를 '1차 캐시'라고 한다.
영속 상태의 entity를 이곳에 저장하고, 1차 캐시의 키는 식별자 값(기본키)이고, 값은 entity 인스턴스이다.
Car car = entityManager.find(Car.class, "car1");
조회 순서는 아래와 같다.
영속성 컨텍스트는 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 수정할 때는 조회 후 데이터를 변경하면 된다.
변경 감지는 영속성 컨텍스트가 관리하는 영속 상태의 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를 지우는게 아니라 변경 내용을 데이터베이스와 동기화 하는 것이다.
@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 불가 / 에러 발생
}
}
- 한번에 끝내는 Java/Spring 웹 개발 마스터 초격차 - 패스트캠퍼스
- JPA 영속성 컨텍스트란?