영속성 컨텍스트 (EntityManager)
- 엔티티를 영구 저장하는 환경
- EntityManager.persist(entity);
먼저 persist()의 역할을 알아보면 EntityManager 를 사용해서 엔티티를 영속성 컨텍스트에 저장하는것! ( DB에 저장 X)
엔티티의 생명주기
비영속 : 영속성 컨텍스트와 전혀 관계가 없는 새로운 상태
영속 : 영속성 컨텍스트에서 관리되는 상태
준영속 : 영속성 컨텍스트에 저장되었다가 분리된 상태
1. 비영속
member 객체를 생성한 상태 (jpa도 아니고 DB에도 들어가지 않은 아무것도 아닌 상태)
Member member = new Member();
member.setId("member1");
member.setUsername("회원1");
2. 영속
//객체를 생성한 상태(비영속)
Member member = new Member();
member.setId("member1");
member.setUsername("회원1");
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
//객체를 저장한 상태(영속)
em.persist(member);
em.persist(member); 는 객체를 영속성 컨텍스트에 저장은 했으나 DB에 저장된 상태는 아니다.
영속상태가 되었다고 DB에 저장된게 아니라
트랜잭션을 커밋하는 시점에 영속성 컨텍스트에 있는 객체가 DB에 쿼리가 날라간다.
(또한 em.find를 처럼 jpa를 통해 조회했을때도 영속성 컨텍스트에 없으면 저장은 되어 영속성 상태는 된다.)
3. 준영속 그리고 삭제
- 준영속 상태란?
영속상태의 엔티티가 영속성 컨텍스트에서 분리되는 것
영속성 컨텍스트가 제공하는 기능(더티체킹..)을 사용못한다.
- 준영속 상태로 만드는 방법
- em.detach(member) : 영속성 컨텍스트 안에서 member 엔티티를 분리(삭제)해서 아무것도 아닌 상태(준영속 상태)로 만드는 것
- em.clear() : 영속성 컨텍스트 전체를 완전히 초기화
- em.close() : 영속성 컨텍스트를 종료 (데이터를 변경해도 안됨)
- 왜 준영속 상태로 만드는가? jpa가 관리할 필요가 없는 엔티티를 준영속 상태로 만든다
삭제
- em.remove(member) 는 객체를 삭제한 상태 (DB에서 삭제)
영속성 컨텍스트의 이점
- 1차 캐시
- 동일성(identity) 보장
- 트랜잭션을 지원하는 쓰기 지연
- 변경 감지 (더티 체킹)
- 지연 로딩
1. 1차 캐시
//객체를 생성한 상태(비영속)
Member member = new Member();
member.setId("member1");
member.setUsername("회원1");
//객체를 저장한 상태(영속)
em.persist(member);
키가 'member1' 이고 em.persist(member) 를 통해 저장한 객체가 Entity에 들어감
1차 캐시에서 조회할때 DB에서 값을 가져오는게 아니라 먼저 1차 캐시에서 member 엔티티를 가져온다.
//객체를 생성한 상태(비영속)
Member member = new Member();
member.setId("member1");
member.setUsername("회원1");
//1차 캐시에 저장됨
em.persist(member);
//1차 캐시에 조회
Member findMember = em.find(Member.class, "member1");
만약 findMember2 엔티티가 DB에는 있고 1차 캐시에는 없다면?
Member findMember2 = em.find(Member.class, "member2");
1. 1차 캐시에 먼저 조회를 하고 없다면
2. DB에 findMember2 찾아 조회한다
3. 1차 캐시에 저장시키고
4. member2 를 반환한다.
이제 findMember2 를 또 조회를 한다면 DB가 아니라 1차 캐시에서 들고온다
2. 동일성 보장 (1차 캐시에서 들고왔기 때문에 동일함)
* 단, 같은 트랜잭션 안에서 비교해야 동일하다
3. 트랜잭션을 지원하는 쓰기 지연
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin(); //DB 트랜잭션 시작
em.persist(memberA);
em.persist(memberB);
//여기까지 INSERT SQL을 DB에 보내지 않는다
tx.commit(); //트랜잭션 커밋하는 순간 INSERT SQL을 DB에 보낸다.
em.persist() 하는 시점에서 영속성 컨텍스트에 memberA,B가 담기고
쓰기지연 SQL 저장소에 memberA, memberB의 INSERT 문(쿼리문)이 저장이 된다.
그 다음 tx.commit();
커밋하는 시점에서 쓰기지연 SQL 저장소가 DB에 flush 해서 쿼리문이 반영이 된다.
아래의 flush 는 쓰기 지연 SQL 저장소의 쿼리(등록,수정,삭제 쿼리)를 DB에 전송
왜 em.persist 할때 쿼리문이 안 날라갈까?
모았다가 DB에 저장하기 위해!
flush
: 영속성 컨텍스트의 변경내용을 DB에 날려주는 것
영속성 컨텍스트를 플러시 하는 방법(2가지)
1. em.flush() - 직접 호출
2. 트랜잭션 커밋 - 플러시 자동 호출
flush 의 역할
1. 영속성 컨텍스트를 비우지 않는다
2. 영속성 컨텍스트의 변경내용을 DB에 동기화 역할
3. 트랜잭션이라는 작업 단위가 중요하다 (커밋 직전에만 동기화하면 된다. )
4. 변경 감지 (더티 체킹)
EntityManagerFactory emf = Persistence.createEntityManagerFactory(
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin(); //DB 트랜잭션 시작
try{
//영속성 컨텍스트 조회
Member member = em.find(Member.class, "memberA");
//영속성 컨텍스트 수정
member.setName("회원1");
//em.persist(member);
//JPA는 변경감지 하여 업데이트 쿼리문을 날린다
//커밋
tx.commit();
}catch {
tx.rollback();
}finally {
em.close;
}
em.update(member) 을 할 필요가 없음.
커밋하는 시점에 1차 캐시와 스냅샷을 비교한다
1차캐시에는 '엔티티(memberA )'와 영속성 컨텍스트에 들어온 최초 상태인 '스냅샷'이 있는데
이때 memberA 를 변경을 하고 커밋하는 시점에 JPA가 엔티티와 스냅샷을 비교한다.
memberA 가 바껴있으면 업데이트 쿼리를 쓰기 지연 SQL 저장소에 만들고
쓰기 지연 SQL 저장소에 있는 업데이트 쿼리를 DB에 반영하고 커밋한다
엔티티 삭제
'공부' 카테고리의 다른 글
엔티티 매핑 (2) | @Id 기본 키 매핑 (0) | 2023.09.22 |
---|---|
엔티티 매핑 (1) | @Entity, @Table, @Column, @Enumerated.. (0) | 2023.09.22 |
JPA (2) | JPA 구동방식 설명 (0) | 2023.09.20 |
JPA (1) | 자바 컬렉션과 JPA의 등장배경, JPA 장점 (0) | 2023.09.08 |
메이븐 프로젝트 설정, JPA 설정 (0) | 2023.07.31 |