목차
Persist Context (영속성 컨텍스트) 란?
JPA 가 Entity 를 관리하기 위한 공간, Entity 상태가 Database 에 반영되기 전까지 Entity 의 읽기/쓰기/수정/삭제를 상태 관리한다.
EntityManagerFactory
에서 EntityManager
를 생성하고 EntityManager 를 이용해 영속성 컨텍스트에 접근할 수 있다.
EntityManager 를 이용해 Entity(데이터)를 저장하거나 조회하면 영속성 컨텍스트에서 해당 Entity 를 관리하게 된다.
EntityManager 는 Transaction 단위로 영속성 컨텍스트를 생성하고 지운다.
EntityManager 와 영속성 Context가 1:1로 대응이 된다.
영속성 Context는 내부에 1차 cache가 존재하고 (key, value) 형태로 데이터를 관리한다. 장점 : buffering이나 Caching을 할 수 있다.
@Id
Entity
Member1
member
영속성 컨텍스트의 이점
1차 캐시
Database 에서 읽어온 데이터를 임시적으로 저장하고 관리한다.
동일성 보장
같은 Key 를 통해 조회할때 반환되는 객체는 항상 같다.
트랜잭션을 지원하는 쓰기 지연
Entity 에 대한 변경 작업이 끝나면 Database 에 반영한다.
변경 감지
Entity 가 변경이 일어나면 Database 에 반영한다. 변경이 없을 경우에는 반영하지 않는다.
지연 로딩
실제 객체를 호출 전까지는 Proxy 객체를 Loading 해 놓는다.
1차 캐시에서 조회 조회를 했지만 select 쿼리가 나가지 않는다. persist로 저장 시에는 1차 캐시에 데이터가 저장되기 때문이다. 조회를 할 때 DB를 조회 한 것이 아닌 우선적으로 1차 캐시를 조회해 값을 가져오기 때문에 DB에 select 쿼리가 나가지 않았던 것이다.
public class JpaMain { public static void main (String[] args) { EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello" ); EntityManager em = emf.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); try { Member member = new Member (); member.setId(101L ); member.setName("HelloJPA" ); em.persist(member); Member findMember = em.find(Member.class, 101L ); System.out.println("findMember.id = " + findMember.getId()); System.out.println("findMember.name = " + findMember.getName()); tx.commit(); }catch (Exception e){ tx.rollback(); }finally { em.close(); } emf.close(); } }
Member Entity 를 조회 했지만 따로 Select 쿼리문이 나가지 않고, 새로운 데이터를 넣기 위한 Insert 문이 발생힌다.
findMember.id = 101 findMember.name = HelloJPA Hibernate: insert into Member (name, id) values (?, ?)
1차 캐시에 data가 없는 경우
DB에서 조회
1차 Cache에 데이터를 저장(있으면)했으면 해당 객체를 가져와서 사용
1차 Cache에 없을 경우 Database 에 접근해 데이터를 가져온 후 1차 Cache 에 저장한다.
똑같은 Key(조회조건) 를 가지고 조회 했을 때 쿼리가 한번만 나가고 그 다음에는 1차 캐시(영속성 컨텍스트) 에 저장된 Entity 객체를 가져온다.
public class JpaMain { public static void main (String[] args) { EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello" ); EntityManager em = emf.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); try { Member findMember1 = em.find(Member.class, 101L ); Member findMember2 = em.find(Member.class, 101L ); tx.commit(); }catch (Exception e){ tx.rollback(); }finally { em.close(); } emf.close(); } }
두번의 조회를 했지만 하나의 select 문만 발생하는 것을 확인할 수 있다.
Hibernate: select member0_.id as id1_0_0_, member0_.name as name2_0_0_ from Member member0_ where member0_.id=?
영속 엔티티의 동일성 보장 Member findMember1 = em.find(Member.class, 101L ); Member findMember2 = em.find(Member.class, 101L ); System.out.println("result = " + (findMember1 == findMember2));
트랜잭션을 지원하는 쓰기 지연 commit()을 실행했을 때 query문이 날라가는 것을 확인할 수 있다.
public class JpaMain { public static void main (String[] args) { EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello" ); EntityManager em = emf.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); try { Member member1 = new Member (150L , "A" ); Member member2 = new Member (160L , "B" ); em.persist(member1); em.persist(member2); System.out.println("==========================" ); tx.commit(); }catch (Exception e){ tx.rollback(); }finally { em.close(); } emf.close(); } }
Hibernate: insert into Member (name, id) values (?, ?) Hibernate: insert into Member (name, id) values (?, ?)
엔티티 수정 public class JpaMain { public static void main (String[] args) { EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello" ); EntityManager em = emf.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); try { Member member = em.find(Member.class, 150L ); member.setName("ZZZZZ" ); System.out.println("==========================" ); tx.commit(); }catch (Exception e){ tx.rollback(); }finally { em.close(); } emf.close(); } }
Hibernate: select member0_.id as id1_0_0_, member0_.name as name2_0_0_ from Member member0_ where member0_.id=? ========================== Hibernate: update Member set name=? where id=?