엔티티(Entity)를 영구 저장하는 환경.
어플리케이션과 데이터베이스 사이에서 객체를 보관하는 가상의 저장소 같은 역할.
엔티티 매니저(EntityManager)를 통해 영속성 컨텍스트에 접근.
EntityManager 를 통해 Entity 를 저장, 조회하면 EntityManager 는 영속성 컨텍스트에 해당 Entity 를 보관하고 관리.
영속성 컨텍스트의 생명주기는 아래 이미지와 같이 4가지 상태가 있다.

Member member = new Member();
Entity Manager를 통해서 Entity를 영속성 컨텍스트에 저장한 상태.
해당 객체는 영속성 컨텍스트에 의해 관리된다는 의미.
// 순수 객체
Member member = new Member();
// 영속성 컨텍스트에 저장 -> 영속성 컨텍스트가 관리
EntityManager em;
em.persist(member);
영속성 컨텍스트가 관리하던 상태에서 엔티티를 더이상 관리하지 않는 상태.
준영속 상태의 특징
// 엔티티를 영속성 컨텍스트에서 분리.
em.detach(member);
// 영속성 콘텍스트를 비움(초기화).
em.claer();
// 영속성 콘텍스트를 종료.
em.close();
엔티티를 준영속 상태로 전환하는 방법
detach(entity)
특정 엔티티만 준영속 상태로 전환(영속성 컨텍스트로부터 분리)
1차 캐시, 쓰기 지연, SQL 저장소 정보 제거
영속성 컨텍스트 안에서의 Insert, Update 쿼리도 제거되어 DB에 저장되지 않음
clear()
영속성 컨텍스트를 완전히 초기화
영속성 컨텍스트의 모든 엔티티를 준영속 상태로 만듦
영속성 컨텍스트 틀은 유지하지만 내용은 비워 새로 만든 것과 같은 상태
close()
영속성 컨텍스트를 종료
해당 영속성 컨텍스트가 관리하던 영속성 상태의 엔티티들은 모두 준영속 상태로 변경
엔티티를 영속 상태로 전환하는 방법
merge(entity)
준영속 상태의 엔티티를 다시 영속 상태로 변경(병합)
파라미터로 전달된 엔티티의 식별자 값으로 영속성 컨텍스트를 조회하고 엔티티가 없다면, DB에서 조회
만약 DB에서도 없다면 새로운 엔티티를 생성하여 병합
병합은 save(저장) 또는 update(수정) 기능 수행
em.remove(member);
엔티티 매니저(Entity Manager)를 생성할 때 영속성 컨텍스트(Persistence Context)도 생성(1:1)
엔티티 매니저를 통해 해당 영속성 컨텍스트에 접근, 관리 할 수 있음
엔티티를 식별자 값(@id로 매핑한 값)으로 구분
영속성 상태에는 식별자 값이 반드시 있어야 한다.(없으면 예외 발생)
트랜잭션을 커밋하는 순간 영속성 컨텍스트에 새로 저장된 엔티티(Entity)를 DB에 반영
영속성 상태에서 값을 여러 번 바꾸어도 마지막 트랜잭션을 커밋하는 순간 값으로 반영
플러시(flush)
영속성 상태의 엔티티는 모두 영속성 컨텍스트에서 관리
아래와 같이 간단한 Member 엔티티를 예로 들어보겠습니다.
@Entity
@Getter
@Setter
public class Member {
@Id
private String id;
private String name;
private String phoneNumber;
}
// 엔티티 생성(비영속=순수객체)
Member member = new Member();
member.setId("member1");
member.setName("개발자");
member.setPhoneNumber("01012345678");
Member member2 = new Member();
member2.setId("member2");
member2.setName("이순신");
member2.setPhoneNumber("01099995555");
// 엔티티 영속
EntityManager em;
em.persist(member); // id = member1
em.persist(member2); // id = member2
영속성 컨텍스트는 내부에 캐시(Cache)가 존재(1차 캐시)
영속 상태의 엔티티는 해당 캐시에 저장
캐시는 Map 형태로 구성되어 있으며, 키(Key)는 @Id로 매핑한 식별자이며 값(Value)은 엔티티 인스턴스
1차 캐시의 키는 식별자 값
식별자 값은 DB(데이터베이스) 기본키와 매핑
영속성 컨텍스트에서 데이터를 저장하고 조회하는 모든 기준은 DB 기본 키 값
Member member = em.find(Member.class, "member1"); // id = member1
1차 캐시에서 해당 Key에 대한 엔티티가 있는지 조회

엔티티가 1차 캐시에 없다면, DB에서 조회

영속성 엔티티의 동일성 보장
Member m1 = em.find(Member.class, "member1");
Member m2 = em.find(Member.class, "member1");
System.out.println(a == b) // true (동일성 보장)
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
// 데이터 변경 시 트랜잭션을 시작.
transaction.begin();
em.persist(member1);
em.persist(member2);
// 여기까지는 INSERT 쿼리를 DB에 보내지 않음.
// 커밋하는 순간 DB에 INSERT 쿼리를 보냄.
transaction.commit();
트랜잭션(Transaction)을 지원하는 쓰기 지연


플러시(flush)
트랜잭션이 커밋될 때 엔티티 매니저는 flush 를 실행.
트랜잭션 커밋 요청 -> 엔티티 매니저 flush 실행 -> DB에 쿼리 요청(동기화) -> DB 커밋
트랜잭션 범위 안에서 실행되며, 등록 쿼리를 아무리 생성해도 트랜잭션 커밋을 하지 않으면 DB에 저장되지 않음.

EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
// 데이터 변경 시 트랜잭션을 시작.
transaction.begin();
// 엔티티 조회(영속성)
Member member1 = em.find(Member.class, "member1");
// 엔티티 데이터 수정
memberA.setName("Developer");
memberA.setPhoneNumber("01052528282");
// 커밋하는 순간 DB에 UPDATE 쿼리를 보냄.
transaction.commit();
JPA 에서는 엔티티를 수정할 때는 엔티티를 조회하여 데이터를 변경하여 저장.(update() 메소드가 없음)
변경 감지 기능을 통해 DB에 자동 반영.(DB Update)

수정 순서
트랜잭션 커밋 요청.(엔티티 매니저 내부에서 먼저 플러시 호출)
엔티티 스냅샷과 비교하여 변경된 엔티티를 찾음.
변경된 엔티티가 발견되면 Update 쿼리를 생성하여 쓰기 지연 SQL 저장소에 저장.
다시 플러시를 호출하면서 쓰기 지연 저장소의 쿼리를 DB에 요청.
DB 커밋.
업데이트의 기본 전략
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
// 데이터 변경 시 트랜잭션을 시작.
transaction.begin();
// 엔티티 조회(영속성)
Member member1 = em.find(Member.class, "member1");
// 엔티티 데이터 삭제
em.remove(member1);
// 커밋하는 순간 DB에 DELETE 쿼리를 보냄.
transaction.commit();
엔티티를 삭제하기 위해서도 먼저 삭제 대상의 엔티티 조회가 필요.
DB에서 바로 삭제되는 것이 아니라, 영속성 컨텍스트(엔티티 매니저)에서만 제거.
DELETE 쿼리를 쓰기 지연 SQL 저장소에 저장.
트랜잭션 커밋 시 쿼리를 DB 에 요청.