의미를 해석해보자면 "엔티티를 영구 저장하는 환경" 이라는 뜻으로 눈에 보이지 않는 논리적인 개념이다, EntityManager를 통해서 영속성 컨텍스트에 접근한다.
EntityManager.persist(entity); // 영속화
EntityManager를 생성하면 1:1로 PersistenceContext라는 공간이 생성되어지고, Entity를 DB에 저장하는것이 아니라 PersistenceContext에 저장한다.
비영속(new/transient)
//객체를 생성한 상태 ( 비영속 )
Member member = new Member();
member.setId("member1");
member.setName("username");
객체를 생성만 한 상태로 JPA와 전혀 관계없는 새로운 상태
영속(managed)
//객체를 생성한 상태 ( 비영속 )
Member member = new Member();
member.setId("member1");
member.setName("username");
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
//객체를 저장한 상태(영속)
em.persist(member);
생성한 member객체를 EntityManager를 통해 persist하여 PersistenceContext에 저장한 상태
준영속(detached)
em.detach(member); //특정 엔티티만 준영속 상태로 전환
em.clear(); // 영속성 컨텍스트를 완전히 초기화
em.close(); // 영속성 컨텍스트를 종료
영속성 컨텍스트에 저장되었다가 분리된 상태
삭제(removed)
em.remove(member);
삭제된 상태
//객체를 생성한 상태 ( 비영속 )
Member member = new Member();
member.setId("member1");
member.setName("username");
.
.
.
//객체를 저장한 상태(영속)
em.persist(member); // 1차 캐시에 저장됨
// 1차 캐시에서 조회
Member findMember = em.find(Member.class, "member1");
// DB에서 조회
Member findMember2 = em.find(Member.class, "member2"); // DB조회 후 1차캐시에 저장
EntityManager는 transaction단위로 동작하기 때문에 고객의 요청 후 비즈니스가 끝나게되면 영속성 컨텍스트는 지워지고, 사용자 모두가 공유하는 캐시가 아닌 1차 캐시이기 때문에 큰 성능상의 이점을 기대하긴 힘들지만 비즈니스 로직이 복잡한 경우에는 이점을 얻을 수도 있다.
📌 영속 엔티티의 동일성 보장
Member a = em.find(Memeber.class, "Member1");
Member b = em.find(Member.class, "Memeber2");
a == b // true
Java Collection에서 값을 가져오는것과 같이 같은 Reference의 객체를 가져오면 같은 값을 보여준다, 이는 위에서 설명한 1차캐시가 있기 때문에 가능하다.
em.persist(memberA)를 실행하면 JPA가 memberA를 분석하여 Insert SQL을 생상하여 쓰기지연 SQL저장소에 저장한다. memberB도 마찬가지이다.
쓰기 지연 SQL저장소에 차곡차곡 쌓인 SQL은 transaction.commit() 명령과 함께 flush 되어 한번에 실행되어진다.
💡그럼 왜 한번에 모아서 보낼까?
DB에 아무리 insert하더라도 commit되지 않으면 반영이 안되기때문에 commit이전에만 실행되면 된다 또한 실행 될 때마다 SQL을 보내서 실행하게 되면 최적화 할 수 있는 여지가 사라진다.
Hibernate에서는 옵션으로 설정한 size만큼 SQL을 모아서 flush한다
<propert name="hibernate.jdbc.batch_size" value="10" />
버퍼링같은 기능으로 모았다가 DB로 저장함으로써 이점을 얻을 수 있다.
JPA의 목적은 Java Collection다루듯이 객체를 다루는 것이다.
그렇기 때문에 update라는 특정 명령없이 객체를 set하여 변경된 내용을 DB에 반영한다.
이러한 동작이 가능한 이유는 1차 캐시 안에는 스냅샷이란 공간이 존재하기 때문이다.
스냅샷은 값을 최초로 읽어온 시점의 상태를 저장하고, flush되는 시점에 Entity와 스냅샷을 비교하여 즉 최초 시점의 Entity와 flush시점의 Entity를 비교하여 변경되었으면 내부적으로 update쿼리를 생성하여 실행한다.
📌 flush
영속성 컨텍스트의 변경내용을 데이터베이스에 반영한다
JPA는 사용자가 요청을 하면 EntityManagerFactory에서 EntityManager를 생성하여 비즈니스 로직을 처리하는데 영속성 컨텍스트(PersistenceContext)라는 논리적인 공간을 사용하여 객체들을 관리하게 된다.
영속성 컨텍스트는 트렌젝션 단위 안에서 작동하게 되며 비즈니스가 끝나면 사라지게 된다.
또한 영속성 컨텍스트 내부에는 1차캐시와 쓰기 지연 SQL 저장소를 가지고있는데 이를 통해 1차 캐시 조회, 쓰기 지연, 변경 감지 같은 기능을 사용하여 여러가지 이점을 제공한다.
데이터의 조회, 등록, 수정, 삭제시 Java 객체 다루듯 내부적으로 비교하여 SQL문을 개발자가 작성하지 않아도 최적화된 SQL문을 생성하여 DB에 요청한다.
📚 참고 및 자료 출처 : 자바 ORM 표준 JPA 프로그래밍 - 기본편 (김영한)