엔티티를 영구 저장하는 환경
- 엔티티 매니저(Entity Manager)로 엔티티를 저장하거나 조회하면 엔티티 매니저는 영속성 컨텍스트에 엔티티를 보관하고 관리한다.
- em.persist(member);
- persist() 메소드는 엔티티 매니저를 사용해서 회원 엔티티를 영속성 컨택스트에 저장한다.
- 영속성 컨텍스트는 논리적인 개념이다.
- 영속성 컨텍스트는 엔티티 매니저를 생성할 때마다 하나씩 만들어진다.
- 엔티티 매니저를 통해서 영속성 컨텍스트에 접근할 수 있고, 영속성 컨텍스트를 관리할 수 있다.
1. 엔티티의 생명 주기
(1) 비영속(new/transient)
영속성 컨텍스트와 전혀 관계가 없는 새로운 상태
Member member = new Member();
member.setId(1L);
member.setName("MemberA");
- 엔티티 객체를 생성
- 순수한 객체 상태이며 아직 정장하지 않았다.
- 영속성 컨텍스트나 데이터베이스와는 전혀 관련이 없다.
(2) 영속(managed)
영속성 컨텍스트에 관리되는 상태
em.persist(member);
Member finMember = em.find(Member.class, 1L);
List<Member> result = em.createQuery("select m from Member as m", Member.class).getResultList();
- 엔티티 매니저를 통해서 엔티티를 영속성 컨텍스트에 저장
- 영속성 컨텍스트가 관리하는 엔티티를 영속 상태라 한다.
- em.find()나 JPQL을 사용해서 조회한 엔티티도 영속성 컨텍스트가 관리하는 영속 상태이다.
(3) 준영속(detached)
영속성 컨텍스트에 저장되었다가 분리된 상태
em.detach(member); // 영속성 컨텍스트에서 분리
em.clear(); // 영속성 컨텍스트 초기화
em.close(); // 영속성 컨텍스트 닫기
- 영속성 컨텍스트가 관리하던 영속 상태의 엔티티를 영속성 컨텍스트가 관리하지 않으면 준영속 상태가 된다.
- 영속성 컨텍스트가 제공하는 기능을 사용하지 못한다.
- 위 3가지 방법으로 준영속 상태를 만들 수 있다.
(4) 삭제(removed)
삭제된 상태
em.remove(member); // 객체 삭제
- 엔티티를 영속성 컨텍스트와 데이터베이스에서 삭제한다.
2. 영속성 컨텍스트의 장점
(1) 1차 캐시
//비영속
Member member = new Member();
member.setId(1L);
member.setName("MemberA");
//영속
em.persist(member);
- 영속성 컨텍스트는 내부에 캐시를 가지고 있는데 이것은 1차 캐시라 한다.
- 영속 상태의 엔티티는 모두 이곳에 저장된다.
- 영속성 컨텍스트 내부에 Map이 하나 있다고 생각하면 된다.
- Key : @Id로 매핑한 식별자(데이터베이스의 기본키)
- Value : 엔티티 인스턴스
- 영속성 컨텍스트에 데이터를 저장하고 조회하는 모든 기준은 데이터베이스 기본 키 값이다.
- EntityManger.find() 메서드 정의
- public <T> T find(Class<T> entityClass, Object primaryKey);
* 1차 캐시에서 조회
//1차 캐시에서 조회
Member member = new Member();
member.setId(1L);
member.setName("MemberA");
//1차 캐시에 저장된다.
em.persist(member);
//1차 캐시에서 조회
Member findMember = em.find(Member.class, 1L);
- em.find()를 호추하면 우선 1차 캐시에서 식별자 값으로 엔티티를 찾는다.
- 찾는 엔티티가 있으면 데이터베이스를 조회하지 않고 메모리에 있는 1차 캐시에서 엔티티를 조회한다.
*데이터베이스에서 조회
//데이터베이스에서 조회
Member findMember2 = em.find(Member.class, 2L);
- em.find()를 호출했는데 엔티티가 1차 캐시에 없으면 엔티티 매니저는 데이터베이스를 조회해서 엔티티를 생성한다.
- 1차 캐시에 저장한 후 영속상태의 엔티티를 반환한다.
(2) 영속 엔티티의 동일성 보장
//영속 엔티티의 동일성 보장
Member findMember1 = em.find(Member.class, 101L);
Member findMember2 = em.find(Member.class, 101l);
System.out.println(findMember2==findMember1); //true
- em.find()를 반복해서 호출하는 경우 영속성 컨텍스트는 1차 캐시에 있는 같은 엔티티 인스턴스를 반환한다.
- 따라서 둘은 같은 인스턴스이므로 true이다.
- 영속성 컨텍스트는 엔티티의 동일성을 보장한다.
(3) 트랜잭션을 지원하는 쓰기 지연(Transactional write-behind)
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
//엔티티 매니저는 데이터 변경 시 트랜잭션을 시작해야 한다.
tx.begin(); //트랜잭션 시작
Member member1 = new Member(150L, "A");
Member member2 = new Member(160L, "B");
em.persist(member1);
em.persist(member2);
//여기까지 INSERT SQL을 데이터베이스에 보내지 않는다.
tx.commit(); //커밋하는 순간 데이터베이스에 INSERT SQL을 보낸다.
- 엔티티 매니저는 트랜잭션을 커밋하기 직전까지 데이터베이스에 엔티티를 저장하지 않고 내부 쓰기 지연 SQL 저장소에 INSERT SQL을 모아둔다.
- 트랜잭션을 커밋하면 엔티티 매니저는 영속성 컨텍스트를 플러시 한다.
- 플러시는 영속성 컨텍스트의 변경 내용을 데이터베이스에 동기화하는 작업
- 플러시를 통해 등록, 수정, 삭제한 엔티티를 데이터베이스에 반영한다.
(4) 변경 감지(Dirty Checking)
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
Member member = em.find(Member.class, "memberA");
//영속 엔티티 데이터 수정
member.setName("JPA");
tx.commit();
- JPA로 엔티티를 수정할 때는 단순히 엔티티를 조회해서 데이터만 변경하면 된다.
- 트랜잭션 커밋 직전에 주석으로 처리된 em.update(member) 메서드를 실행해야 할 것 같지만 이런 메서드는 없다.
- 트랜잭션을 커밋하면 엔티티 매니저 내부에서 먼저 플러시가 호출된다.
- 엔티티와 스냅샷을 비교해서 변경된 엔티티를 찾는다.
- 변경된 엔티티가 있으면 수정 쿼리를 생성해서 쓰기 지연 SQL 저장소에 보낸다.
- 쓰기 지연 저장소의 SQL을 데이터베이스에 보낸다.
- 데이터베이스 트랜잭션을 커밋한다.
4. 플러시 (Flush)
영속성 컨텍스트의 변경 내용을 데이터베이스에 반영한다.
- 영속성 컨텍스트를 비우지 않는다.
- 영속성 컨테스트의 변경내용을 데이터베이스에 동기화
(1) 플러시 실행 시 동작
- 변경 감지가 동작해서 영속성 컨텍스트에 있는 모든 엔티티를 스냅샷과 비교해서 수정된 엔티티를 찾는다.
- 수정된 엔티티는 수정 쿼리를 만들어 쓰기 지연 SQL 저장소에 등록한다.
- 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송한다.
(2) 영속성 컨텍스를 플러시하는 방법
- em.flush() 직접 호출
- 트랜잭션 커밋 시 플러시 자동 호출
- JPQL 쿼리 실행 시 플러시 자동 호출
* 식별자를 기준으로 조회하는 find() 메서드를 호출할 때는 플러시가 실행되지 않는다.
Member member1 = new Member(300L, "JPA");
em.persist(member1);
Member member = em.find(Member.class, 1L);
System.out.println("================");
tx.commit();
em.find() 호출 시에 select query는 즉시 나간다.
하지만 이전에 영속성 컨텍스트에 등록되어 있던 member1은 플러시 되지 않는다.
이후 커밋할 때 데이터베이스에 반영된다.
반응형
'JPA > JPA' 카테고리의 다른 글
[JPA] 상속 관계 매핑 (0) | 2022.01.03 |
---|---|
[JPA] 연관관계 매핑 - 2 (0) | 2022.01.02 |
[JPA] 연관관계 매핑 - 1 (0) | 2022.01.01 |
[JPA] 엔티티 매핑 (Entity Mapping) (0) | 2021.12.31 |
JPA 사용하기 (feat. Maven) (0) | 2021.12.28 |
댓글