"엔티티를 영구 저장하는 환경" 이라는 뜻을 가집니다.
Entity Manager를 통해서 접근 가능합니다.
영속성 컨텍스트에 관리되는 엔티티의 상태를 영속 상태라고 합니다.
예) EntityManager.persist(entity);
Entity Manager Factory는 생성되는 시점에 DB 커넥션 풀을 생성 한 뒤, 요청이 들어올 때 마다 Entity Manager를 생성합니다.
Entity Manager는 DB 연결이 필요할 때 커넥션 풀에 있는 connection을 얻습니다.
생성 비용 때문입니다. Entity Manager Factory는 생성할 때 DB 커넥션 풀을 생성하기 때문에 생성 비용이 큽니다. 반면, Entity Manager는 생성 비용이 거의 들지 않기 때문에 Entity Manager Factory는 Entity Manager를 사용합니다.
Entity Manager Factory는 Thread Safe 합니다.
즉, 여러 쓰레드가 접근하더라도 안전합니다.
반면에, Entity Manager는 여러 쓰레드가 동시에 접근할 경우 동시성 문제가 발생하기 때문에 절대로 공유하면 안됩니다.
//객체를 생성한 상태 (비영속)
Member member = new Member();
member.setId("member1");
member.setUsername("회원1");
//객체를 생성한 상태(비영속)
Member member = new Member();
member.setId("member1");
member.setUsername("회원1");
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
//객체를 저장한 상태(영속)
em.persist(member);
//회원 엔티티를 영속성 컨텍스트에서 분리, 준영속 상태
em.detach(member);
//객체를 삭제한 상태(삭제)
em.remove(member);
Member member = new Member();
member.setId("member1");
member.setUsername("회원1");
//1차 캐시에 저장됨
em.persist(member);
//1차 캐시에서 조회
Member findMember = em.find(Member.class, "member1");
Member findMember2 = em.find(Member.class, "member2");
조회 순서
- "member1" 조회 요청 시 JPA는 영속성 컨텍스트의 1차 캐시에서 해당 PK 값을 가진 Member 객체를 찾습니다.
- 1차 캐시에 존재하면 그 값을 반환하고, 존재하지 않으면 DB를 조회하여 값을 가져옵니다.
- DB를 조회하여 값을 가져온 경우, 1차 캐시에 값을 저장합니다.
- 그 이후에 "member1" 조회가 들어오면 DB를 거치지 않고 1차 캐시에서 값을 가져옵니다.
Member a = em.find(Member.class, "member1");
Member b = em.find(Member.class, "member1");
System.out.println(a == b); //동일성 비교 true
Member a = em.find(Member.class, "member1");
em.clear(); //영속성 컨텍스트를 비워줌
Member b = em.find(Member.class, "member1");
System.out.println(a == b); //동일성 비교 false
Member a = em.find(Member.class, "member1");
Member b = em.find(Member.class, "member1");
em.clear(); //영속성 컨텍스트를 비워줌
System.out.println(a == b); //동일성 비교 true
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
//엔티티 매니저는 데이터 변경 시 트랜잭션을 시작해야 합니다.
transaction.begin(); // [트랜잭션] 시작
em.persist(memberA);
em.persist(memberB);
//여기까지 INSERT SQL을 데이터베이스에 보내지 않습니다.
//커밋하는 순간 데이터베이스에 INSERT SQL을 보냅니다.
trasaction.commit(); // [트랜잭션] 커밋
영속성 컨텍스트에는 1차 캐시 뿐만 아니라 쓰기 지연 저장소가 존재합니다.
em.persist(memberA), em.persist(memberB) 를 하면 INSERT SQL이 생성되어 쓰기 지연 저장소에 저장됩니다.
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin(); // [트랜잭션] 시작
//영속 엔티티 조회
Member memberA = em.find(Member.class, "memberA");
//영속 엔티티 데이터 수정
memberA.setUsername("hi");
memberA.setAge(10);
//em.update(member) 이런 코드가 있어야 하지 않을까?
transaction.commit(); // [트랜잭션] 커밋]
엔티티가 1차 캐시에 저장될 때, 저장되는 시점의 상태를 스냅샷으로 만들어서 1차 캐시에 보관합니다.
트랜잭션 커밋 시점에 엔티티와 스냅샷을 비교합니다.
엔티티가 스냅샷과 차이가 있을 경우 JPA는 이를 감지하여 DB에 반영합니다.
[Reference]