📌 영속성 컨텍스트
영속성 컨텍스트(Persistence Context)란 엔티티를 영구 저장하는 환경을 말한다.
영속성 컨텍스트는 애플리케이션과 데이터베이스 사이에서 객체를 보관하는 가상의 데이터베이스 같은 역할을 한다. 엔티티 매니저를 통해 엔티티를 저장하거나 조회하면 엔티티 메니저는 영속성 컨텍스트에 엔티티를 보관하고 관리한다.
스프링에서는 JPA(Java 혹은 Jakarta Persistence API)라는 영속성 인터페이스를 제공한다. 스프링에서 사용하는 Hibernate ORM은 추상화된 JPA를 구현화한 구현체이다. Hibernate ORM을 통해 내부적으로 JDBC API를 이용해 데이터베이스에 접근한다.
✔️ 영속성 컨텍스트의 구조
✔️ 영속성 컨텍스트의 장점
- 1차 캐시
데이터의 변동에 대해 DB에 바로 업데이트되지 않고 영속성 컨텍스트 내부에 있는 캐시를 거쳐 DB를 업데이트하는 것을 말한다.
아래 코드만 실행했을 때 DB에 실제 반영되는 것이 아닌 1차 캐시 공간에 영속한 엔티티를 저장해놓는다.
Member member = new Member("hgd@gmail.com", "Hong Gil Dong", "010-2222-3333");
Order order = new Order();
member.addOrder(order);
order.addMember(member);
em.persist(member);
em.persist(order);
flush()나 commit이 안됐기에 실제로 DB에 반영이 안된 모습
영속 엔티티는 1차 캐시 공간에 저장되어 있다.
- 동일성을 보장
영속성 콘텍스트는 영속 엔티티의 동일성을 보장한다.
동일성은 값 뿐만 아니라 실제 인스턴스 자체가 같다는 뜻을 의미한다.
Coffee findCoffee1 = em.find("Coffee.class", "1L");
Coffee findCoffee2 = em.find("Coffee.class", "1L");
System.out.println(findCoffee1 == findCoffee2);
// 결과 값 : true
- 트랜잭션을 지원하는 쓰기 지연
영속된 엔티티의 값이 변경될 때 DB에 바로 업데이트 하지 않는다.
해당 변경 사항은 쓰기지연 SQL 저장소에 쿼리문으로 저장해놨다가 EntityManager의 flush()나 Transaction의 commit()을 통해 한꺼번에 보내지게 된다.
tx.begin();
Member member = new Member("hgd@gmail.com", "Hong Gil Dong", "010-2222-3333");
Order order = new Order();
member.addOrder(order);
order.addMember(member);
em.persist(member);
em.persist(order);
tx.commit();
commit()을 통해 DB에 반영된 모습
쓰기 지연 SQL 저장소에 있는 쿼리가 모두 실행된 모습이다.
- 변경 감지(Dirty Checking)
엔티티가 수정이 일어나도 개발자는 영속성 컨텍스트에 따로 알려줄 필요없이 영속성 컨텍스트가 알아서 변경사항을 체크해준다.
Dirty Checking의 흐름
1. 트랜잭션을 커밋하면 EntityManager의 내부에서 플러시가 호출된다.
2. 엔티티와 스냅샷을 비교하여 변경된 엔티티를 찾는다.
3. 변경된 엔티티가 있으면 수정 쿼리를 생성해 쓰기 지연 SQL 저장소에 저장한다.
4. 쓰기 지연 저장소의 SQL을 플러시한다.
5. 데이터베이스 트랜잭션을 커밋
✔️ 쓰기지연
쓰기지연(Transactional write-behind)이란 영속성 컨텍스트에 저장된 객체가 변경이 발생됐을 때, 데이터베이스에 바로 쿼리를 보내는 것이 아니라, SQL 쿼리를 버퍼에 모아두다가 영속성 컨텍스트가 flush하는 시점에 모아둔 SQL 쿼리를 데이터베이스로 보내는 기능을 말한다.
마치 공사 현장에서 자재를 옮길 때 손수레를 쓰는 것과 같다. 자재를 하나씩 옮기는 것보다 손수레처럼 버퍼에 쌓아두었다가 한 번에 처리하는 것이 더욱 빠를 수 밖에 없다.
✔️ 엔티티 생명주기
엔티티 생명주기(Entity LifeCycle)에는 비영속, 영속, 준영속, 삭제가 존재한다.
❗️비영속(new/transient)
엔티티 객체를 생성했지만 아직 영속성 컨테이너를 저장하지 않은 상태를 비영속이라고 한다.
Coffee coffee = new Coffee();
다음과 같이 객체의 선언만 되었을 뿐 영속성 컨텍스트와 아무런 관계가 없는 상태를 비영속 상태라고 한다.
❗️영속(managed)
엔티티 매니저(EntityManager)를 통해 엔티티를 영속성 컨텍스트에 저장한 상태를 말한다. 영속성 컨텍스트에 저장됨에 따라 해당 객체는 영속성 컨텍스트에 의해 관리된다.
// EntityManager은 EntityManageFactory의 createEntityManager 메서드에 의해 만들어진다.
// em : EntityManager em = emFactory.createEntityManager;
em.persist(coffee);
❗️준영속(detached)
영속성 컨테이너에서 관리하던 영속 상태의 엔티티를 더이상 관리하지 않는다고 하면 준영속 상태가 된다.
// 영속성 컨텍스트 내의 특정 엔티티를 준영속 상태로 만듦
em.detach(coffee);
// 영속성 컨텍스트 안을 모두 비워 엔티티를 모두 준영속 상태로 만듦
em.clear();
// 영속성 컨텍스트를 종료시켜 엔티티를 모두 준영속 상태로 만듦
em.close();
준영속 상태가 된 엔티티는 영속성 컨텍스트의 기능들을 모두 사용할 수 없다. 또한 준영속 상태라는 별도의 식별자 값을 부여받는다.
❗️삭제(removed)
엔티티를 영속성 컨텍스트와 데이터베이스에서 모두 삭제한다.
em.remove(coffee);
댓글